#include "wx/wxprec.h"
#include "wx/defs.h"
#include "wx/object.h"
#include "wx/hash.h"
#include "wx/string.h"
#include "wx/log.h"
#include "wx/event.h"
#include "wx/window.h"
#include "wx/panel.h"
#include "wx/dc.h"
#include "wx/dcmemory.h"
#include "wx/button.h"
#include "wx/pen.h"
#include "wx/brush.h"
#include "wx/cursor.h"
#include "wx/dialog.h"
#include "wx/settings.h"
#include "wx/msgdlg.h"
#include "wx/choice.h"
#include "wx/stattext.h"
#include "wx/scrolwin.h"
#include "wx/dirdlg.h"
#include "wx/layout.h"
#include "wx/sizer.h"
#include "wx/textdlg.h"
#include "wx/filedlg.h"
#include "wx/statusbr.h"
#include "wx/intl.h"
#include "wx/frame.h"

#define __wxPG_SOURCE_FILE__

#include <wx/propgrid/propgrid.h>
#include <wx/propgrid/propdev.h>
#include <wx/propgrid/editors.h>

#if wxPG_USE_RENDERER_NATIVE
#include <wx/renderer.h>
#endif

#if wxPG_USING_WXOWNERDRAWNCOMBOBOX
#include <wx/odcombo.h>
#else
#include <wx/propgrid/odcombo.h>
#endif

#include "wx/timer.h"
#include "wx/dcbuffer.h"
#include "wx/image.h"
#include <wx/clipbrd.h>
#include <wx/dataobj.h>

#define wxPG_ALLOW_CLIPPING  1
#define wxPG_GUTTER_DIV  3
#define wxPG_GUTTER_MIN  3
#define wxPG_YSPACING_MIN  1
#define wxPG_DEFAULT_VSPACING  2
#define wxPG_OPTIMAL_WIDTH  200
#define wxPG_CAPRECTXMARGIN  2
#define wxPG_CAPRECTYMARGIN  1
#define PWC_CHILD_SUMMARY_LIMIT  16
#define PWC_CHILD_SUMMARY_CHAR_LIMIT  64
#define wxPG_MIN_SCROLLBAR_WIDTH  10


#define wxPG_DEFAULT_CURSOR  wxNullCursor



#define wxPG_DEFAULT_SPLITTERX  110
#define wxPG_NO_CHILD_EVT_MOTION  1
#define wxPG_CUSTOM_IMAGE_WIDTH  20
#define wxPG_ALLOW_EMPTY_TOOLTIPS  0
#define wxPG_NAT_BUTTON_BORDER_ANY  1
#define wxPG_NAT_BUTTON_BORDER_X  1
#define wxPG_NAT_BUTTON_BORDER_Y  1
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT  1


#define wxPG_SPLITTERX_DETECTMARGIN1  3
#define wxPG_SPLITTERX_DETECTMARGIN2  2

#define wxPG_DRAG_MARGIN  30
#define wxPG_CUSTOM_IMAGE_SPACINGY  1
#define wxPG_HIDER_BUTTON_HEIGHT  25
#define wxPG_PIXELS_PER_UNIT  m_lineHeight
#define DEFAULT_IMAGE_OFFSET_INCREMENT  (wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2)
#ifdef wxPG_ICON_WIDTH
#define m_iconHeight m_iconWidth
#endif

#define wxPG_TOOLTIP_DELAY  1000
#define IN_CELL_EXPANDER_BUTTON_X_ADJUST  2

void wxPropertyGrid::AutoGetTranslation( bool enable ) {
  wxPGGlobalVars->m_autoGetTranslation = enable;
}

static bool wxPGIsWindowBuffered( const wxWindow* wnd ) {
  return wnd->IsDoubleBuffered();
}

const wxEventType wxEVT_PG_SELECTED = wxNewEventType();
const wxEventType wxEVT_PG_CHANGING = wxNewEventType();
const wxEventType wxEVT_PG_CHANGED = wxNewEventType();
const wxEventType wxEVT_PG_HIGHLIGHTED = wxNewEventType();
const wxEventType wxEVT_PG_RIGHT_CLICK = wxNewEventType();
const wxEventType wxEVT_PG_PAGE_CHANGED = wxNewEventType();
const wxEventType wxEVT_PG_ITEM_EXPANDED = wxNewEventType();
const wxEventType wxEVT_PG_ITEM_COLLAPSED = wxNewEventType();
const wxEventType wxEVT_PG_DOUBLE_CLICK = wxNewEventType();
const wxEventType wxEVT_PG_LABEL_EDIT_BEGIN = wxNewEventType();
const wxEventType wxEVT_PG_LABEL_EDIT_ENDING = wxNewEventType();
const wxChar *wxPropertyGridNameStr = _T( "wxPropertyGrid" );
const wxChar *wxPGTypeName_long = _T( "long" );
const wxChar *wxPGTypeName_bool = _T( "bool" );
const wxChar *wxPGTypeName_double = _T( "double" );
const wxChar *wxPGTypeName_wxString = _T( "string" );
const wxChar *wxPGTypeName_void = _T( "void*" );
const wxChar *wxPGTypeName_wxArrayString = _T( "arrstring" );
static const wxChar* gs_noCellText = _T( "@!" );

static void wxPGDrawFocusRect( wxDC& dc, const wxRect& rect ) {
  dc.SetLogicalFunction( wxINVERT );
  dc.SetPen( wxPen( *wxBLACK, 1, wxDOT ) );
  dc.SetBrush( *wxTRANSPARENT_BRUSH );
  dc.DrawRectangle( rect );
  dc.SetLogicalFunction( wxCOPY );
}

#include "wx/tokenzr.h"

static wxArrayString wxSplit( const wxString& str, const wxChar sep, const wxChar escape ) {
  if( escape == _T( '\0' ) ) {
    return wxStringTokenize( str, sep, wxTOKEN_RET_EMPTY_ALL );
  }
  wxArrayString ret;
  wxString curr;
  wxChar prev = _T( '\0' );
  for( wxString::const_iterator i = str.begin(), end = str.end(); i != end; ++i ) {
    const wxChar ch = *i;
    if( ch == sep ) {
      if( prev == escape ) {
        *curr.rbegin() = sep;
      } else {
        ret.push_back( curr );
        curr.clear();
      }
    } else {
      curr += ch;
    }
    prev = ch;
  }
  if( !curr.empty() || prev == sep ) {
    ret.Add( curr );
  }
  return ret;
}

void wxPropertyGridInterface::AddPropertyChoice( wxPGPropArg id, const wxString& label, int value ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  p->InsertChoice( label, -1, value );
}

void wxPropertyGridInterface::InsertPropertyChoice( wxPGPropArg id, const wxString& label, int index, int value ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  p->InsertChoice( label, index, value );
}

void wxPropertyGridInterface::DeletePropertyChoice( wxPGPropArg id,
    int index ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  p->DeleteChoice( index );
}

class wxPGGlobalVarsClassManager {
  public:
    wxPGGlobalVarsClassManager() {}
    ~wxPGGlobalVarsClassManager() { delete wxPGGlobalVars; }
};

static wxPGGlobalVarsClassManager gs_pgGlobalVarsClassManager;
wxPGGlobalVarsClass* wxPGGlobalVars = ( wxPGGlobalVarsClass* ) NULL;

wxPGGlobalVarsClass::wxPGGlobalVarsClass() {
  m_boolChoices.Add( _T( "False" ) );
  m_boolChoices.Add( _T( "True" ) );
  m_fontFamilyChoices = ( wxPGChoices* ) NULL;
  m_defaultRenderer = new wxPGDefaultRenderer();
  m_autoGetTranslation = false;
  m_offline = 0;
  m_extraStyle = 0;
  wxVariant v;
  v = ( long )0;
  wxVariantClassInfo_long = wxPGVariantDataGetClassInfo( v.GetData() );
  v = wxString();
  wxVariantClassInfo_string = wxPGVariantDataGetClassInfo( v.GetData() );
  v = ( double )0.0;
  wxVariantClassInfo_double = wxPGVariantDataGetClassInfo( v.GetData() );
  v = ( bool )false;
  wxVariantClassInfo_bool = wxPGVariantDataGetClassInfo( v.GetData() );
  v = wxArrayString();
  wxVariantClassInfo_arrstring = wxPGVariantDataGetClassInfo( v.GetData() );
  wxColour col;
  wxVariant v2( ( wxObject* )&col );
  wxVariantClassInfo_wxobject = wxPGVariantDataGetClassInfo( v2.GetData() );
  wxVariantList list;
  v = wxVariant( list );
  wxVariantClassInfo_list = wxPGVariantDataGetClassInfo( v.GetData() );
  v << *wxRED;
  wxVariantClassInfo_wxColour = wxPGVariantDataGetClassInfo( v.GetData() );
  #if wxUSE_DATETIME
  v = wxVariant( wxDateTime::Now() );
  wxVariantClassInfo_datetime = wxPGVariantDataGetClassInfo( v.GetData() );
  #endif
  m_vEmptyString = wxString();
  m_vZero = ( long ) 0;
  m_vMinusOne = ( long ) - 1;
  m_vTrue = true;
  m_vFalse = false;
  m_strDefaultValue = _T( "DefaultValue" );
  m_strMin = _T( "Min" );
  m_strMax = _T( "Max" );
  m_strUnits = _T( "Units" );
  m_strInlineHelp = _T( "InlineHelp" );
  #ifdef __WXDEBUG__
  m_warnings = 0;
  #endif
}


wxPGGlobalVarsClass::~wxPGGlobalVarsClass() {
  size_t i;
  delete m_defaultRenderer;
  delete m_fontFamilyChoices;
  #if wxUSE_VALIDATORS
  for( i = 0; i < m_arrValidators.GetCount(); i++ ) {
    delete( ( wxValidator* )m_arrValidators[i] );
  }
  #endif
  wxPGHashMapS2P::iterator vt_it;
  for( vt_it = m_mapEditorClasses.begin(); vt_it != m_mapEditorClasses.end(); ++vt_it ) {
    wxPGEditor* editor = ( wxPGEditor* ) vt_it->second;
    delete editor;
  }
}

IMPLEMENT_ABSTRACT_CLASS( wxPGProperty, wxObject )
wxString wxString_wxPG_LABEL = _T( "_LABEL_AS_NAME" );

void wxPGProperty::Init() {
  m_commonValue = -1;
  m_arrIndex = 0xFFFF;
  m_parent = NULL;
  m_parentState = ( wxPropertyGridState* ) NULL;
  m_clientData = NULL;
  m_customEditor = ( wxPGEditor* ) NULL;
  m_validator = ( wxValidator* ) NULL;
  m_valueBitmap = ( wxBitmap* ) NULL;
  m_maxLen = 0;
  m_flags = wxPG_PROP_PROPERTY;
  m_depth = 1;
  m_bgColIndex = 0;
  m_fgColIndex = 0;
  SetExpanded( true );
}


void wxPGProperty::Init( const wxString& label, const wxString& name ) {
  m_label = label;
  #ifndef __WXPYTHON__
  if( name != wxString_wxPG_LABEL )
  #else
  if( ( &name != ( ( wxString* )NULL ) ) && name != _T( "_LABEL_AS_NAME" ) )
  #endif
    DoSetName( name );
  else {
    DoSetName( m_label );
  }
  Init();
}

wxPGProperty::wxPGProperty()
  : wxObject() {
  Init();
}

wxPGProperty::wxPGProperty( const wxString& label, const wxString& name ) : wxObject() {
  Init( label, name );
}

wxPGProperty::~wxPGProperty() {
  DoEmpty();
  delete m_valueBitmap;
  delete m_validator;
  unsigned int i;
  for( i = 0; i < m_cells.size(); i++ ) {
    delete( wxPGCell* ) m_cells[i];
  }
  m_parent = NULL;
}


bool wxPGProperty::IsSomeParent( wxPGProperty* candidate ) const {
  wxPGProperty* parent = m_parent;
  do {
    if( parent == candidate ) {
      return true;
    }
    parent = parent->m_parent;
  } while( parent );
  return false;
}

void wxPGProperty::SetName( const wxString& newName ) {
  wxPropertyGrid* pg = GetGrid();
  if( pg ) {
    pg->SetPropertyName( this, newName );
  } else {
    DoSetName( newName );
  }
}

wxString wxPGProperty::GetName() const {
  wxPGProperty* parent = GetParent();
  if( !m_name.length() || !parent || parent->IsCategory() || parent->IsRoot() ) {
    return m_name;
  }
  return m_parent->GetName() + _T( "." ) + m_name;
}

wxPropertyGrid* wxPGProperty::GetGrid() const {
  if( !m_parentState ) {
    return NULL;
  }
  return m_parentState->GetGrid();
}

bool wxPGProperty::ValidateValue( wxVariant & ( value ), wxPGValidationInfo & ( validationInfo ) ) const {
  return true;
}

void wxPGProperty::OnSetValue() {
}

void wxPGProperty::RefreshChildren() {
}

void wxPGProperty::OnValidationFailure( wxVariant & ( pendingValue ) ) {
}

wxString wxPGProperty::GetColumnText( unsigned int col ) const {
  wxPGCell* cell = GetCell( col );
  if( cell ) {
    return cell->GetText();
  } else {
    if( col == 0 ) {
      return GetLabel();
    } else if( col == 1 ) {
      return GetDisplayedString();
    } else if( col == 2 ) {
      return GetAttribute( wxPGGlobalVars->m_strUnits, wxEmptyString );
    }
  }
  return wxEmptyString;
}

void wxPGProperty::GenerateComposedValue( wxString& text, int argFlags ) const {
  int i;
  int iMax = m_children.GetCount();
  text.clear();
  if( iMax == 0 ) {
    return;
  }
  if( iMax > PWC_CHILD_SUMMARY_LIMIT &&
      !( argFlags & wxPG_FULL_VALUE ) ) {
    iMax = PWC_CHILD_SUMMARY_LIMIT;
  }
  int iMaxMinusOne = iMax - 1;
  if( !IsTextEditable() ) {
    argFlags |= wxPG_UNEDITABLE_COMPOSITE_FRAGMENT;
  }
  wxPGProperty* curChild = ( wxPGProperty* ) m_children.Item( 0 );
  for( i = 0; i < iMax; i++ ) {
    wxString s;
    if( !curChild->IsValueUnspecified() ) {
      s = curChild->GetValueString( argFlags | wxPG_COMPOSITE_FRAGMENT );
    }
    bool skip = false;
    if( ( argFlags & wxPG_UNEDITABLE_COMPOSITE_FRAGMENT ) && !s.length() ) {
      skip = true;
    }
    if( !curChild->GetChildCount() || skip ) {
      text += s;
    } else {
      text += _T( "[" ) + s + _T( "]" );
    }
    if( i < iMaxMinusOne ) {
      if( text.length() > PWC_CHILD_SUMMARY_CHAR_LIMIT &&
          !( argFlags & wxPG_EDITABLE_VALUE ) &&
          !( argFlags & wxPG_FULL_VALUE ) ) {
        break;
      }
      if( !skip ) {
        if( !curChild->GetChildCount() ) {
          text += _T( "; " );
        } else {
          text += _T( " " );
        }
      }
      curChild = ( wxPGProperty* ) m_children.Item( i + 1 );
    }
  }
  if( ( unsigned int )i < m_children.GetCount() ) {
    if( !wxPG_String_EndsWith( text, _T( "; " ) ) ) {
      text += _T( "; ..." );
    } else {
      text += _T( "..." );
    }
  }
}

wxString wxPGProperty::GetValueAsString( int argFlags ) const {
  wxCHECK_MSG( GetCount() > 0,
               wxString(),
               _T( "If user property does not have any children, it must override GetValueAsString" ) );
  wxString text;
  GenerateComposedValue( text, argFlags );
  return text;
}

wxString wxPGProperty::GetValueString( int argFlags ) const {
  wxPropertyGrid* pg = GetGrid();
  if( IsValueUnspecified() ) {
    return pg->GetUnspecifiedValueText( argFlags );
  }
  if( m_commonValue == -1 ) {
    return GetValueAsString( argFlags );
  }
  const wxPGCommonValue* cv = pg->GetCommonValue( m_commonValue );
  if( argFlags & wxPG_FULL_VALUE ) {
    return cv->GetLabel();
  } else if( argFlags & wxPG_EDITABLE_VALUE ) {
    return cv->GetEditableText();
  } else {
    return cv->GetLabel();
  }
}

bool wxPGProperty::IntToValue( wxVariant& variant, int number, int ( argFlags ) ) const {
  variant = ( long )number;
  return true;
}

bool wxPGProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const {
  if( !GetCount() ) {
    return false;
  }
  unsigned int curChild = 0;
  unsigned int iMax = m_children.GetCount();
  if( iMax > PWC_CHILD_SUMMARY_LIMIT &&
      !( argFlags & wxPG_FULL_VALUE ) ) {
    iMax = PWC_CHILD_SUMMARY_LIMIT;
  }
  bool changed = false;
  wxString token;
  size_t pos = 0;
  bool addOnlyIfNotEmpty = false;
  const wxChar delimeter = _T( ';' );
  size_t tokenStart = 0xFFFFFF;
  wxVariantList temp_list;
  wxVariant list( temp_list );
  int propagatedFlags = argFlags & ( wxPG_REPORT_ERROR | wxPG_PROGRAMMATIC_VALUE );
  #ifdef __WXDEBUG__
  bool debug_print = false;
  #endif
  #ifdef __WXDEBUG__
  if( debug_print ) {
    wxLogDebug( _T( ">> %s.StringToValue('%s')" ), GetLabel().c_str(), text.c_str() );
  }
  #endif
  wxString::const_iterator it = text.begin();
  wxUniChar a;
  if( it != text.end() ) {
    a = *it;
  } else {
    a = 0;
  }
  for( ;; ) {
    unsigned int strPosIncrement = 1;
    if( tokenStart != 0xFFFFFF ) {
      if( a == delimeter || a == 0 ) {
        token = text.substr( tokenStart, pos - tokenStart );
        token.Trim( true );
        size_t len = token.length();
        if( !addOnlyIfNotEmpty || len > 0 ) {
          const wxPGProperty* child = Item( curChild );
          wxVariant variant( child->GetValue() );
          wxString childName( child->GetBaseName() );
          #ifdef __WXDEBUG__
          if( debug_print ) {
            wxLogDebug( _T( "token = '%s', child = %s" ), token.c_str(), childName.c_str() );
          }
          #endif
          if( ( argFlags & wxPG_PROGRAMMATIC_VALUE ) ||
              !child->HasFlag( wxPG_PROP_DISABLED | wxPG_PROP_READONLY ) ) {
            if( len > 0 ) {
              if( child->ActualStringToValue( variant, token,
                                              propagatedFlags | wxPG_COMPOSITE_FRAGMENT ) ) {
                variant.SetName( childName );
                list.Append( variant );
                changed = true;
              }
            } else {
              variant.MakeNull();
              variant.SetName( childName );
              list.Append( variant );
              changed = true;
            }
          }
          curChild++;
          if( curChild >= iMax ) {
            break;
          }
        }
        tokenStart = 0xFFFFFF;
      }
    } else {
      if( a != _T( ' ' ) ) {
        addOnlyIfNotEmpty = false;
        if( a == _T( '[' ) ) {
          int depth = 1;
          if( it != text.end() ) {
            it++;
          }
          pos++;
          size_t startPos = pos;
          while( it != text.end() && depth > 0 ) {
            a = *it;
            it++;
            pos++;
            if( a == _T( ']' ) )
            { depth--; }
            else if( a == _T( '[' ) )
            { depth++; }
          }
          token = text.substr( startPos, pos - startPos - 1 );
          if( !token.length() ) {
            break;
          }
          const wxPGProperty* child = Item( curChild );
          if( ( argFlags & wxPG_PROGRAMMATIC_VALUE ) ||
              !child->HasFlag( wxPG_PROP_DISABLED | wxPG_PROP_READONLY ) ) {
            wxVariant variant( child->GetValue() );
            if( child->ActualStringToValue( variant, token, propagatedFlags ) ) {
              variant.SetName( child->GetBaseName() );
              list.Append( variant );
              changed = true;
            } else {
              variant.MakeNull();
              list.Append( variant );
              changed = true;
            }
          }
          curChild++;
          if( curChild >= iMax ) {
            break;
          }
          addOnlyIfNotEmpty = true;
          tokenStart = 0xFFFFFF;
        } else {
          tokenStart = pos;
          if( a == delimeter ) {
            strPosIncrement--;
          }
        }
      }
    }
    if( a == 0 ) {
      break;
    }
    it += strPosIncrement;
    if( it != text.end() ) {
      a = *it;
    } else
    { a = 0; }
    pos += strPosIncrement;
  }
  if( changed ) {
    variant = list;
  }
  return changed;
}

bool wxPGProperty::SetValueFromString( const wxString& text, int argFlags ) {
  wxVariant variant( m_value );
  bool res = ActualStringToValue( variant, text, argFlags );
  if( res ) {
    SetValue( variant );
  }
  return res;
}

bool wxPGProperty::SetValueFromInt( long number, int argFlags ) {
  wxVariant variant( m_value );
  bool res = ActualIntToValue( variant, number, argFlags );
  if( res ) {
    SetValue( variant );
  }
  return res;
}

wxSize wxPGProperty::OnMeasureImage( int ( item ) ) const {
  if( m_valueBitmap ) {
    return wxSize( m_valueBitmap->GetWidth(), -1 );
  }
  return wxSize( 0, 0 );
}

int wxPGProperty::GetImageOffset( int imageWidth ) const {
  int imageOffset = 0;
  if( imageWidth ) {
    if( imageWidth <= ( wxPG_CUSTOM_IMAGE_WIDTH + 5 ) ) {
      imageOffset = imageWidth + DEFAULT_IMAGE_OFFSET_INCREMENT;
    } else
    { imageOffset = imageWidth + 1; }
  }
  return imageOffset;
}

wxPGCellRenderer* wxPGProperty::GetCellRenderer( int ( column ) ) const {
  return wxPGGlobalVars->m_defaultRenderer;
}

void wxPGProperty::OnCustomPaint( wxDC& dc, const wxRect& rect, wxPGPaintData& ) {
  wxBitmap* bmp = m_valueBitmap;
  wxCHECK_RET( bmp && bmp->Ok(), _T( "invalid bitmap" ) );
  wxCHECK_RET( rect.x >= 0, _T( "unexpected measure call" ) );
  dc.DrawBitmap( *bmp, rect.x, rect.y );
}

const wxPGEditor* wxPGProperty::DoGetEditorClass() const {
  return wxPG_EDITOR( TextCtrl );
}

bool wxPGProperty::OnEvent( wxPropertyGrid*, wxWindow*, wxEvent& ) {
  return false;
}

void wxPGProperty::SetValue( wxVariant value, wxVariant* pList, int flags ) {
  if( value.IsNull() && ( flags & wxPG_SETVAL_BY_USER ) &&
      !UsesAutoUnspecified() ) {
    value = GetDefaultValue();
  }
  if( !value.IsNull() ) {
    wxVariant tempListVariant;
    SetCommonValue( -1 );
    if( wxPGIsVariantType( value, list ) ) {
      if( HasFlag( wxPG_PROP_COMPOSED_VALUE ) ) {
        tempListVariant = value;
        pList = &tempListVariant;
      }
      wxVariant newValue;
      AdaptListToValue( value, &newValue );
      value = newValue;
    }
    if( HasFlag( wxPG_PROP_AGGREGATE ) ) {
      flags |= wxPG_SETVAL_AGGREGATED;
    }
    if( pList && !pList->IsNull() ) {
      wxASSERT( wxPGIsVariantType( *pList, list ) );
      wxASSERT( GetChildCount() );
      wxASSERT( !IsCategory() );
      wxVariantList& list = pList->GetList();
      wxVariantList::iterator node;
      unsigned int i = 0;
      for( node = list.begin(); node != list.end(); node++ ) {
        wxVariant& childValue = *( ( wxVariant* )*node );
        wxPGProperty* child = GetPropertyByNameWH( childValue.GetName(), i );
        if( child ) {
          if( wxPGIsVariantType( childValue, list ) ) {
            if( child->HasFlag( wxPG_PROP_AGGREGATE ) && !( flags & wxPG_SETVAL_AGGREGATED ) ) {
              wxVariant listRefCopy = childValue;
              child->SetValue( childValue, &listRefCopy, flags | wxPG_SETVAL_FROM_PARENT );
            } else {
              wxVariant oldVal = child->GetValue();
              child->SetValue( oldVal, &childValue, flags | wxPG_SETVAL_FROM_PARENT );
            }
          } else if( !wxPG_VARIANT_EQ( child->GetValue(), childValue ) ) {
            if( !IsFlagSet( wxPG_PROP_AGGREGATE ) )
            { child->SetValue( childValue, NULL, flags | wxPG_SETVAL_FROM_PARENT ); }
            if( flags & wxPG_SETVAL_BY_USER )
            { child->SetFlag( wxPG_PROP_MODIFIED ); }
          }
        }
        i++;
      }
    }
    if( !value.IsNull() ) {
      wxPGVariantAssign( m_value, value );
      OnSetValue();
    }
    if( flags & wxPG_SETVAL_BY_USER ) {
      SetFlag( wxPG_PROP_MODIFIED );
    }
    if( IsFlagSet( wxPG_PROP_AGGREGATE ) ) {
      RefreshChildren();
    }
  } else {
    if( m_commonValue != -1 ) {
      wxPropertyGrid* pg = GetGrid();
      if( !pg || m_commonValue != pg->GetUnspecifiedCommonValue() ) {
        SetCommonValue( -1 );
      }
    }
    m_value = value;
    if( AreChildrenComponents() ) {
      unsigned int i;
      for( i = 0; i < GetChildCount(); i++ ) {
        Item( i )->SetValue( value, NULL, flags | wxPG_SETVAL_FROM_PARENT );
      }
    }
  }
  if( !( flags & wxPG_SETVAL_FROM_PARENT ) ) {
    UpdateParentValues();
  }
  if( flags & wxPG_SETVAL_REFRESH_EDITOR ) {
    wxPropertyGrid* pg = GetGridIfDisplayed();
    if( pg ) {
      wxPGProperty* selected = pg->GetSelectedProperty();
      if( selected && ( selected == this || selected->IsSomeParent( this ) || this->IsSomeParent( selected ) ) ) {
        RefreshEditor();
      }
      pg->DrawItemAndValueRelated( this );
    }
  }
}

void wxPGProperty::SetValueInEvent( wxVariant value ) const {
  GetGrid()->ValueChangeInEvent( value );
}

void wxPGProperty::SetFlagRecursively( FlagType flag, bool set ) {
  ChangeFlag( flag, set );
  unsigned int i;
  for( i = 0; i < GetChildCount(); i++ ) {
    Item( i )->SetFlagRecursively( flag, set );
  }
}

void wxPGProperty::RefreshEditor() {
  if( !m_parent ) {
    return;
  }
  wxPropertyGrid* pg = GetGrid();
  if( pg && pg->GetSelectedProperty() == this ) {
    pg->RefreshEditor();
  }
}

wxVariant wxPGProperty::GetDefaultValue() const {
  wxVariant defVal = GetAttribute( wxPG_ATTR_DEFAULT_VALUE );
  if( !defVal.IsNull() ) {
    return defVal;
  }
  wxVariant value = GetValue();
  if( !value.IsNull() ) {
    wxPGVariantDataClassInfo classInfo = wxPGVariantDataGetClassInfo( value.GetData() );
    if( wxPGIsVariantClassInfo( classInfo, long ) ) {
      return wxPGVariant_Zero;
    }
    if( wxPGIsVariantClassInfo( classInfo, string ) ) {
      return wxPGVariant_EmptyString;
    }
    if( wxPGIsVariantClassInfo( classInfo, bool ) ) {
      return wxPGVariant_False;
    }
    if( wxPGIsVariantClassInfo( classInfo, double ) ) {
      return wxVariant( 0.0 );
    }
    wxPGVariantData* pgvdata = wxDynamicCastVariantData( m_value.GetData(), wxPGVariantData );
    if( pgvdata ) {
      return pgvdata->GetDefaultValue();
    }
    if( wxPGIsVariantClassInfo( classInfo, arrstring ) ) {
      return wxVariant( wxArrayString() );
    }
    if( wxPGIsVariantClassInfo( classInfo, wxColour ) ) {
      return WXVARIANT( *wxRED );
    }
    #if wxUSE_DATETIME
    if( wxPGIsVariantClassInfo( classInfo, datetime ) ) {
      return wxVariant( wxDateTime::Now() );
    }
    #endif
    wxFAIL_MSG( wxString::Format( _T( "Inorder for value to have default value, it must be added to" )
                                  _T( "wxPGProperty::GetDefaultValue or it's variantdata must inherit" )
                                  _T( "from wxPGVariantData (unrecognized type was '%s')" ), m_value.GetType().c_str() )
              );
  }
  return wxVariant();
}

wxPGProperty* wxPGProperty::InsertChild( int index,
    wxPGProperty* childProperty ) {
  if( index < 0 ) {
    index = m_children.size();
  }
  wxASSERT_MSG( m_parentState, _T( "Add property to a grid or page before " )_T( "adding children." ) );
  m_parentState->DoInsert( this, index, childProperty );
  return childProperty;
}

void wxPGProperty::SetCell( int column, wxPGCell* cellObj ) {
  if( column >= ( int )m_cells.size() ) {
    m_cells.SetCount( column + 1, NULL );
  }
  delete( wxPGCell* ) m_cells[column];
  m_cells[column] = cellObj;
}

wxPGCell* wxPGProperty::GetOrCreateCell( unsigned int column ) {
  wxPGCell* cell = GetCell( column );
  if( cell ) {
    return cell;
  }
  wxString text;
  if( column == 0 ) {
    text = m_label;
  }
  cell = new wxPGCell( text );
  SetCell( column, cell );
  return cell;
}

void wxPGProperty::SetChoiceSelection( int newValue,
                                       const wxPGChoiceInfo& choiceInfo ) {
  wxString ts = GetValue().GetType();
  wxCHECK_RET( choiceInfo.m_choices, _T( "invalid choiceinfo" ) );
  if( ts == _T( "long" ) ) {
    SetValue( ( long ) newValue );
  } else if( ts == _T( "string" ) ) {
    SetValue( choiceInfo.m_choices->GetLabel( newValue ) );
  }
}


wxString wxPGProperty::GetChoiceString( unsigned int index ) {
  wxPGChoiceInfo ci;
  GetChoiceInfo( &ci );
  wxCHECK_MSG( ci.m_choices, wxEmptyString, _T( "This property class does not support choices." ) );
  return ci.m_choices->GetLabel( index );
}

int wxPGProperty::InsertChoice( const wxString& label, int index, int value ) {
  wxPropertyGrid* pg = GetGrid();
  wxPGChoiceInfo ci;
  ci.m_choices = ( wxPGChoices* ) NULL;
  int sel = GetChoiceInfo( &ci );
  wxCHECK_MSG( ci.m_choices, -1, _T( "This property class does not support choices." ) );
  int newSel = sel;
  if( index < 0 ) {
    index = ci.m_choices->GetCount();
  }
  if( index <= sel ) {
    newSel++;
  }
  ci.m_choices->Insert( label, index, value );
  if( sel != newSel ) {
    SetChoiceSelection( newSel, ci );
  }
  if( this == pg->GetSelection() ) {
    GetEditorClass()->InsertItem( pg->GetEditorControl(), label, index );
  }
  return index;
}

void wxPGProperty::DeleteChoice( int index ) {
  wxPropertyGrid* pg = GetGrid();
  wxPGChoiceInfo ci;
  ci.m_choices = ( wxPGChoices* ) NULL;
  int sel = GetChoiceInfo( &ci );
  wxCHECK_RET( ci.m_choices, _T( "This property class does not support choices." ) );
  int newSel = sel;
  if( sel == index ) {
    SetValueToUnspecified();
    newSel = 0;
  } else if( index < sel ) {
    newSel--;
  }
  ci.m_choices->RemoveAt( index );
  if( sel != newSel ) {
    SetChoiceSelection( newSel, ci );
  }
  if( this == pg->GetSelection() ) {
    GetEditorClass()->DeleteItem( pg->GetEditorControl(), index );
  }
}

int wxPGProperty::GetChoiceInfo( wxPGChoiceInfo * ( info ) ) {
  return -1;
}

wxPGEditorDialogAdapter* wxPGProperty::GetEditorDialog() const {
  return NULL;
}

bool wxPGProperty::DoSetAttribute( const wxString & ( name ), wxVariant & ( value ) ) {
  return false;
}

void wxPGProperty::SetAttribute( const wxString& name, wxVariant value ) {
  if( DoSetAttribute( name, value ) ) {
    if( wxPGGlobalVars->HasExtraStyle( wxPG_EX_WRITEONLY_BUILTIN_ATTRIBUTES ) ) {
      return;
    }
  }
  m_attributes.Set( name, value );
}

wxVariant wxPGProperty::DoGetAttribute( const wxString & ( name ) ) const {
  return wxVariant();
}


wxVariant wxPGProperty::GetAttribute( const wxString& name ) const {
  return m_attributes.FindValue( name );
}

wxString wxPGProperty::GetAttribute( const wxString& name, const wxString& defVal ) const {
  wxVariant variant = m_attributes.FindValue( name );
  if( !variant.IsNull() ) {
    return variant.GetString();
  }
  return defVal;
}

long wxPGProperty::GetAttributeAsLong( const wxString& name, long defVal ) const {
  wxVariant variant = m_attributes.FindValue( name );
  return wxPGVariantToInt( variant, defVal );
}

double wxPGProperty::GetAttributeAsDouble( const wxString& name, double defVal ) const {
  double retVal;
  wxVariant variant = m_attributes.FindValue( name );
  if( wxPGVariantToDouble( variant, &retVal ) ) {
    return retVal;
  }
  return defVal;
}

wxVariant wxPGProperty::GetAttributesAsList() const {
  wxVariantList tempList;
  wxVariant v( tempList, wxString::Format( _T( "@%s@attr" ), m_name.c_str() ) );
  wxPGAttributeStorage::const_iterator it = m_attributes.StartIteration();
  wxVariant variant;
  while( m_attributes.GetNext( it, variant ) ) {
    v.Append( variant );
  }
  return v;
}

const unsigned int gs_propFlagToStringSize = 14;

static const wxChar* gs_propFlagToString[gs_propFlagToStringSize] = {
  NULL,
  _T( "DISABLED" ),
  _T( "HIDDEN" ),
  NULL,
  _T( "NOEDITOR" ),
  _T( "COLLAPSED" ),
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL
};

wxString wxPGProperty::GetFlagsAsString( FlagType flagsMask ) const {
  wxString s;
  int relevantFlags = m_flags & flagsMask & wxPG_STRING_STORED_FLAGS;
  FlagType a = 1;
  unsigned int i = 0;
  for( i = 0; i < gs_propFlagToStringSize; i++ ) {
    if( relevantFlags & a ) {
      const wxChar* fs = gs_propFlagToString[i];
      wxASSERT( fs );
      if( s.length() ) {
        s << _T( "|" );
      }
      s << fs;
    }
    a = a << 1;
  }
  return s;
}

void wxPGProperty::SetFlagsFromString( const wxString& str ) {
  FlagType flags = 0;
  WX_PG_TOKENIZER1_BEGIN( str, _T( '|' ) )
  unsigned int i;
  for( i = 0; i < gs_propFlagToStringSize; i++ ) {
    const wxChar* fs = gs_propFlagToString[i];
    if( fs && str == fs ) {
      flags |= ( 1 << i );
      break;
    }
  }
  WX_PG_TOKENIZER1_END()
  m_flags = ( m_flags & ~wxPG_STRING_STORED_FLAGS ) | flags;
}

wxValidator* wxPGProperty::DoGetValidator() const {
  return ( wxValidator* ) NULL;
}

wxPGChoices& wxPGProperty::GetChoices() {
  wxPGChoiceInfo choiceInfo;
  choiceInfo.m_choices = NULL;
  GetChoiceInfo( &choiceInfo );
  wxASSERT_MSG( choiceInfo.m_choices, _T( "This property class does not support choices." ) );
  return *choiceInfo.m_choices;
}

const wxPGChoices& wxPGProperty::GetChoices() const {
  return ( const wxPGChoices& )( ( wxPGProperty* )this )->GetChoices();
}

unsigned int wxPGProperty::GetChoiceCount() const {
  wxPGChoiceInfo ci;
  ci.m_choices = NULL;
  ( ( wxPGProperty* )this )->GetChoiceInfo( &ci );
  if( ci.m_choices && ci.m_choices->IsOk() ) {
    return ci.m_choices->GetCount();
  }
  return 0;
}

const wxPGChoiceEntry* wxPGProperty::GetCurrentChoice() const {
  wxPGChoiceInfo ci;
  ci.m_choices = ( wxPGChoices* ) NULL;
  int index = ( ( wxPGProperty* )this )->GetChoiceInfo( &ci );
  if( index == -1 || !ci.m_choices || index >= ( int )ci.m_choices->GetCount() ) {
    return NULL;
  }
  return &( *ci.m_choices )[index];
}

bool wxPGProperty::SetChoices( wxPGChoices& choices ) {
  wxPGChoiceInfo ci;
  ci.m_choices = NULL;
  GetChoiceInfo( &ci );
  wxCHECK_MSG( ci.m_choices, false, _T( "This property class does not support choices." ) );
  wxPropertyGrid* pg = GetGrid();
  if( pg && pg->GetSelection() == this ) {
    pg->ClearSelection();
  }
  ci.m_choices->Assign( choices );
  wxVariant defVal = GetDefaultValue();
  if( defVal.IsNull() ) {
    return false;
  }
  SetValue( defVal );
  return true;
}


const wxPGEditor* wxPGProperty::GetEditorClass() const {
  const wxPGEditor* editor;
  if( !m_customEditor ) {
      editor = DoGetEditorClass();
  } else {
    editor = m_customEditor;
  }
  if( GetDisplayedCommonValueCount() ) {
    if( editor->IsKindOf( CLASSINFO( wxPGTextCtrlAndButtonEditor ) ) ) {
      editor = wxPG_EDITOR( ChoiceAndButton );
    } else if( editor->IsKindOf( CLASSINFO( wxPGTextCtrlEditor ) ) ) {
      editor = wxPG_EDITOR( ComboBox );
    }
  }
  return editor;
}

void wxPGProperty::SetChoicesExclusive() {
  wxPGChoiceInfo ci;
  ci.m_choices = ( wxPGChoices* ) NULL;
  GetChoiceInfo( &ci );
  if( ci.m_choices ) {
    ci.m_choices->SetExclusive();
  }
}

bool wxPGProperty::Hide( bool hide, int flags ) {
  wxPropertyGrid* pg = GetGrid();
  if( pg ) {
    return pg->HideProperty( this, hide, flags );
  }
  return DoHide( hide, flags );
}

bool wxPGProperty::DoHide( bool hide, int flags ) {
  if( !hide ) {
    ClearFlag( wxPG_PROP_HIDDEN );
  } else {
    SetFlag( wxPG_PROP_HIDDEN );
  }
  if( flags & wxPG_RECURSE ) {
    unsigned int i;
    for( i = 0; i < GetChildCount(); i++ ) {
      Item( i )->DoHide( hide, flags | wxPG_RECURSE_STARTS );
    }
  }
  return true;
}

bool wxPGProperty::HasVisibleChildren() const {
  unsigned int i;
  for( i = 0; i < GetChildCount(); i++ ) {
    wxPGProperty* child = Item( i );
    if( !child->HasFlag( wxPG_PROP_HIDDEN ) ) {
      return true;
    }
  }
  return false;
}

bool wxPGProperty::PrepareValueForDialogEditing( wxPropertyGrid* propGrid ) {
  return propGrid->EditorValidate();
}

bool wxPGProperty::RecreateEditor() {
  wxPropertyGrid* pg = GetGrid();
  wxASSERT( pg );
  wxPGProperty* selected = pg->GetSelection();
  if( this == selected ) {
    pg->DoSelectProperty( this, wxPG_SEL_FORCE );
    return true;
  }
  return false;
}

void wxPGProperty::SetValueImage( wxBitmap& bmp ) {
  delete m_valueBitmap;
  if( bmp.Ok() ) {
    wxSize maxSz = GetGrid()->GetImageSize();
    wxSize imSz( bmp.GetWidth(), bmp.GetHeight() );
    if( imSz.y > maxSz.y ) {
      #if wxUSE_IMAGE
      wxImage img = bmp.ConvertToImage();
      double scaleY = ( double )maxSz.y / ( double )imSz.y;
      img.Rescale( ( ( double )bmp.GetWidth() )*scaleY,
                   ( ( double )bmp.GetHeight() )*scaleY,
                   wxIMAGE_QUALITY_HIGH );
      wxBitmap* bmpNew = new wxBitmap( img, 32 );
      #else
      wxBitmap* bmpNew = new wxBitmap( maxSz.x, maxSz.y, bmp.GetDepth() );
      wxMemoryDC dc;
      dc.SelectObject( *bmpNew );
      double scaleY = ( double )maxSz.y / ( double )imSz.y;
      dc.SetUserScale( scaleY, scaleY );
      dc.DrawBitmap( bmp, 0, 0 );
      #endif
      m_valueBitmap = bmpNew;
    } else
    { m_valueBitmap = new wxBitmap( bmp ); }
    m_flags |= wxPG_PROP_CUSTOMIMAGE;
  } else {
    m_valueBitmap = NULL;
    m_flags &= ~( wxPG_PROP_CUSTOMIMAGE );
  }
}

wxPGProperty* wxPGProperty::GetMainParent() const {
  const wxPGProperty* curChild = this;
  const wxPGProperty* curParent = m_parent;
  while( curParent && !curParent->IsCategory() ) {
    curChild = curParent;
    curParent = curParent->m_parent;
  }
  return ( wxPGProperty* ) curChild;
}

const wxPGProperty* wxPGProperty::GetLastVisibleSubItem() const {
  if( !IsExpanded() || !GetChildCount() ) {
    return this;
  }
  return Last()->GetLastVisibleSubItem();
}

bool wxPGProperty::IsVisible() const {
  const wxPGProperty* parent;
  if( HasFlag( wxPG_PROP_HIDDEN ) ) {
    return false;
  }
  for( parent = GetParent(); parent != NULL; parent = parent->GetParent() ) {
    if( !parent->IsExpanded() || parent->HasFlag( wxPG_PROP_HIDDEN ) ) {
      return false;
    }
  }
  return true;
}

wxPropertyGrid* wxPGProperty::GetGridIfDisplayed() const {
  wxPropertyGridState* state = GetParentState();
  if( !state ) {
    return NULL;
  }
  wxPropertyGrid* propGrid = state->GetGrid();
  if( state == propGrid->GetState() ) {
    return propGrid;
  }
  return NULL;
}

int wxPGProperty::GetY2( int lh ) const {
  const wxPGProperty* parent;
  const wxPGProperty* child = this;
  int y = 0;
  for( parent = GetParent(); parent != NULL; parent = child->GetParent() ) {
    if( !parent->IsExpanded() ) {
      return parent->GetY2( lh );
    }
    y += parent->GetChildrenHeight( lh, child->GetIndexInParent() );
    y += lh;
    child = parent;
  }
  y -= lh;
  return y;
}

int wxPGProperty::GetY() const {
  return GetY2( GetGrid()->GetRowHeight() );
}

wxPGProperty* wxPGPropArgCls::GetPtr( wxPropertyGridInterface* methods ) const {
  if( !m_isName ) {
    wxASSERT_MSG( m_ptr.property, _T( "invalid property ptr" ) );
    return m_ptr.property;
  } else if( m_isName == 1 ) {
    return methods->GetPropertyByNameI( *m_ptr.name );
  } else if( m_isName == 2 ) {
    return methods->GetPropertyByNameI( m_ptr.rawname );
  }
  wxASSERT( m_isName <= 3 );
  return NULL;
}

void wxPGProperty::AddChild2( wxPGProperty* prop, int index, bool correct_mode ) {
  if( index < 0 || ( size_t )index >= m_children.GetCount() ) {
    if( correct_mode ) {
      prop->m_arrIndex = m_children.GetCount();
    }
    m_children.Add( prop );
  } else {
    m_children.Insert( prop, index );
    if( correct_mode ) {
      FixIndexesOfChildren( index );
    }
  }
  prop->m_parent = this;
}

void wxPGProperty::AddChild( wxPGProperty* prop ) {
  wxASSERT_MSG( prop->GetBaseName().length(),
                _T( "Property's children must have unique, non-empty names within their scope" ) );
  prop->m_arrIndex = m_children.GetCount();
  m_children.Add( prop );
  int custImgHeight = prop->OnMeasureImage().y;
  if( custImgHeight < 0 ) {
    prop->m_flags |= wxPG_PROP_CUSTOMIMAGE;
  }
  prop->m_parent = this;
}

void wxPGProperty::AdaptListToValue( wxVariant& list, wxVariant* value ) const {
  wxASSERT( GetChildCount() );
  wxASSERT( !IsCategory() );
  *value = GetValue();
  if( !list.GetCount() ) {
    return;
  }
  wxASSERT( GetCount() >= ( unsigned int )list.GetCount() );
  bool allChildrenSpecified;
  if( HasFlag( wxPG_PROP_AGGREGATE ) ) {
    allChildrenSpecified = AreAllChildrenSpecified( &list );
  } else {
    allChildrenSpecified = true;
  }
  wxVariant childValue = list[0];
  unsigned int i;
  unsigned int n = 0;
  for( i = 0; i < GetCount(); i++ ) {
    const wxPGProperty* child = Item( i );
    if( childValue.GetName() == child->GetBaseName() ) {
      if( wxPGIsVariantType( childValue, list ) ) {
        wxVariant cv2( child->GetValue() );
        child->AdaptListToValue( childValue, &cv2 );
        childValue = cv2;
      }
      if( allChildrenSpecified ) {
        ActualChildChanged( *value, i, childValue );
      }
      n++;
      if( n == ( unsigned int )list.GetCount() ) {
        break;
      }
      childValue = list[n];
    }
  }
}

void wxPGProperty::FixIndexesOfChildren( unsigned int starthere ) {
  unsigned int i;
  for( i = starthere; i < GetCount(); i++ ) {
    Item( i )->m_arrIndex = i;
  }
}

wxPGProperty* wxPGProperty::GetPropertyByName( const wxString& name ) const {
  size_t i;
  for( i = 0; i < GetCount(); i++ ) {
    wxPGProperty* p = Item( i );
    if( p->m_name == name ) {
      return p;
    }
  }
  int pos = name.Find( _T( '.' ) );
  if( pos <= 0 ) {
    return ( wxPGProperty* ) NULL;
  }
  wxPGProperty* p = GetPropertyByName( name. substr( 0, pos ) );
  if( !p || !p->GetChildCount() ) {
    return NULL;
  }
  return p->GetPropertyByName( name.substr( pos + 1, name.length() - pos - 1 ) );
}

wxPGProperty* wxPGProperty::GetPropertyByNameWH( const wxString& name, unsigned int hintIndex ) const {
  unsigned int i = hintIndex;
  if( i >= GetCount() ) {
    i = 0;
  }
  unsigned int lastIndex = i - 1;
  if( lastIndex >= GetCount() ) {
    lastIndex = GetCount() - 1;
  }
  for( ;; ) {
    wxPGProperty* p = Item( i );
    if( p->m_name == name ) {
      return p;
    }
    if( i == lastIndex ) {
      break;
    }
    i++;
    if( i == GetCount() ) {
      i = 0;
    }
  };
  return NULL;
}

int wxPGProperty::GetChildrenHeight( int lh, int iMax_ ) const {
  unsigned int i = 0;
  int h = 0;
  if( iMax_ == -1 ) {
    iMax_ = GetChildCount();
  }
  unsigned int iMax = iMax_;
  wxASSERT( iMax <= GetChildCount() );
  if( !IsExpanded() && GetParent() ) {
    return 0;
  }
  while( i < iMax ) {
    wxPGProperty* pwc = ( wxPGProperty* ) Item( i );
    if( !pwc->HasFlag( wxPG_PROP_HIDDEN ) ) {
      if( !pwc->IsExpanded() ||
          pwc->GetChildCount() == 0 ) {
        h += lh;
      } else
      { h += pwc->GetChildrenHeight( lh ) + lh; }
    }
    i++;
  }
  return h;
}

wxPGProperty* wxPGProperty::GetItemAtY( unsigned int y, unsigned int lh, unsigned int* nextItemY ) const {
  wxASSERT( nextItemY );
  wxPGProperty* result = NULL;
  wxPGProperty* current = NULL;
  unsigned int iy = *nextItemY;
  unsigned int i = 0;
  unsigned int iMax = GetCount();
  while( i < iMax ) {
    wxPGProperty* pwc = Item( i );
    if( !pwc->HasFlag( wxPG_PROP_HIDDEN ) ) {
      if( y < iy ) {
        result = current;
        break;
      }
      iy += lh;
      if( pwc->IsExpanded() &&
          pwc->GetChildCount() > 0 ) {
        result = ( wxPGProperty* ) pwc->GetItemAtY( y, lh, &iy );
        if( result ) {
          break;
        }
      }
      current = pwc;
    }
    i++;
  }
  if( !result && y < iy ) {
    result = current;
  }
  *nextItemY = iy;
  return ( wxPGProperty* ) result;
}

void wxPGProperty::DoEmpty() {
  size_t i;
  if( !HasFlag( wxPG_PROP_CHILDREN_ARE_COPIES ) ) {
    for( i = 0; i < GetCount(); i++ ) {
      wxPGProperty* p = ( wxPGProperty* ) Item( i );
      delete p;
    }
  }
  m_children.Empty();
}

void wxPGProperty::DeleteChildren() {
  wxPropertyGridState* state = m_parentState;
  if( !GetChildCount() ) {
    return;
  }
  unsigned int i = GetChildCount();
  while( i > 0 ) {
    i--;
    state->DoDelete( Item( i ), true );
  }
}

bool wxPGProperty::IsChildSelected( const bool recursive ) const {
  size_t i;
  for( i = 0; i < GetChildCount(); i++ ) {
    wxPGProperty* child = Item( i );
    if( m_parentState->DoIsPropertySelected( child ) ) {
      return true;
    }
    if( recursive && child->IsChildSelected( recursive ) ) {
      return true;
    }
  }
  return false;
}

void wxPGProperty::ChildChanged( wxVariant & ( thisValue ), int ( childIndex ), wxVariant & ( childValue ) ) const {
}

bool wxPGProperty::AreAllChildrenSpecified( wxVariant* pendingList ) const {
  unsigned int i;
  const wxVariantList* pList = NULL;
  wxVariantList::const_iterator node;
  if( pendingList ) {
    pList = &pendingList->GetList();
    node = pList->begin();
  }
  for( i = 0; i < GetChildCount(); i++ ) {
    wxPGProperty* child = Item( i );
    const wxVariant* listValue = NULL;
    wxVariant value;
    if( pendingList ) {
      const wxString& childName = child->GetBaseName();
      for( ; node != pList->end(); node++ ) {
        const wxVariant& item = *( ( const wxVariant* )*node );
        if( item.GetName() == childName ) {
          listValue = &item;
          value = item;
          break;
        }
      }
    }
    if( !listValue ) {
      value = child->GetValue();
    }
    if( value.IsNull() ) {
      return false;
    }
    if( child->GetChildCount() ) {
      const wxVariant* childList = NULL;
      if( listValue && wxPGIsVariantType( *listValue, list ) ) {
        childList = listValue;
      }
      if( !child->AreAllChildrenSpecified( ( wxVariant* )childList ) ) {
        return false;
      }
    }
  }
  return true;
}

wxPGProperty* wxPGProperty::UpdateParentValues() {
  wxPGProperty* parent = m_parent;
  if( parent && parent->HasFlag( wxPG_PROP_COMPOSED_VALUE ) &&
      !parent->IsCategory() && !parent->IsRoot() ) {
    wxString s;
    parent->GenerateComposedValue( s, 0 );
    parent->m_value = s;
    return parent->UpdateParentValues();
  }
  return this;
}

bool wxPGProperty::IsTextEditable() const {
  if( HasFlag( wxPG_PROP_READONLY ) ) {
    return false;
  }
  if( HasFlag( wxPG_PROP_NOEDITOR ) &&
      ( GetChildCount() ||
        wxPG_String_EndsWith( wxString( GetEditorClass()->GetClassInfo()->GetClassName() ), _T( "Button" ) ) )
    ) {
    return false;
  }
  return true;
}

WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN( wxPGRootProperty, none, TextCtrl )
IMPLEMENT_DYNAMIC_CLASS( wxPGRootProperty, wxPGProperty )

wxPGRootProperty::wxPGRootProperty()
  : wxPGProperty() {
  #ifdef __WXDEBUG__
  m_name = _T( "<root>" );
  #endif
  SetParentalType( 0 );
  m_depth = 0;
}

wxPGRootProperty::~wxPGRootProperty() {
}

WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN( wxPropertyCategory, none, TextCtrl )
IMPLEMENT_DYNAMIC_CLASS( wxPropertyCategory, wxPGProperty )

void wxPropertyCategory::Init() {
  SetParentalType( wxPG_PROP_CATEGORY );
  m_capFgColIndex = 1;
  m_textExtent = -1;
}

wxPropertyCategory::wxPropertyCategory()
  : wxPGProperty() {
  Init();
}

wxPropertyCategory::wxPropertyCategory( const wxString &label, const wxString& name )
  : wxPGProperty( label, name ) {
  Init();
}

wxPropertyCategory::~wxPropertyCategory() {
}

wxString wxPropertyCategory::GetValueAsString( int ) const {
  return wxEmptyString;
}

int wxPropertyCategory::GetTextExtent( const wxWindow* wnd, const wxFont& font ) const {
  if( m_textExtent > 0 ) {
    return m_textExtent;
  }
  int x = 0, y = 0;
  ( ( wxWindow* )wnd )->GetTextExtent( m_label, &x, &y, 0, 0, &font );
  return x;
}

void wxPropertyCategory::CalculateTextExtent( wxWindow* wnd, const wxFont& font ) {
  int x = 0, y = 0;
  wnd->GetTextExtent( m_label, &x, &y, 0, 0, &font );
  m_textExtent = x;
}

wxSize wxPGCellRenderer::GetImageSize( const wxPGProperty * ( property ), int ( column ), int ( item ) ) const {
  return wxSize( 0, 0 );
}

void wxPGCellRenderer::DrawText( wxDC& dc, const wxRect& rect,
                                 int xOffset, const wxString& text ) const {
  dc.DrawText( text,
               rect.x + xOffset + wxPG_XBEFORETEXT,
               rect.y + ( ( rect.height - dc.GetCharHeight() ) / 2 ) );
}

void wxPGCellRenderer::DrawEditorValue( wxDC& dc, const wxRect& rect, int xOffset, const wxString& text,
                                        wxPGProperty* property, const wxPGEditor* editor ) const {
  int yOffset = ( ( rect.height - dc.GetCharHeight() ) / 2 );
  if( editor ) {
    wxRect rect2( rect );
    rect2.x += xOffset;
    rect2.y += yOffset;
    rect2.height -= yOffset;
    editor->DrawValue( dc, rect2, property, text );
  } else {
    dc.DrawText( text, rect.x + xOffset + wxPG_XBEFORETEXT, rect.y + yOffset );
  }
}

void wxPGCellRenderer::DrawCaptionSelectionRect( wxDC& dc, int x, int y, int w, int h ) const {
  wxRect focusRect( x, y + ( ( h - dc.GetCharHeight() ) / 2 ), w, h );
  wxPGDrawFocusRect( dc, focusRect );
}

int wxPGCellRenderer::PreDrawCell( wxDC& dc, const wxRect& rect, const wxPGCell& cell, int flags ) const {
  int imageWidth = 0;
  if( !( flags & Selected ) ) {
    wxColour fgCol = cell.GetFgCol();
    if( fgCol.Ok() ) {
      dc.SetTextForeground( fgCol );
    }
    wxColour bgCol = cell.GetBgCol();
    if( bgCol.Ok() ) {
      dc.SetPen( bgCol );
      dc.SetBrush( bgCol );
      dc.DrawRectangle( rect );
    }
  }
  const wxFont& font = cell.GetFont();
  if( font.Ok() ) {
    dc.SetFont( font );
  }
  const wxBitmap& bmp = cell.GetBitmap();
  if( bmp.Ok() &&
      ( !( flags & Control ) || bmp.GetHeight() < rect.height )
    ) {
    dc.DrawBitmap( bmp,
                   rect.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
                   rect.y + wxPG_CUSTOM_IMAGE_SPACINGY,
                   true );
    imageWidth = bmp.GetWidth();
  }
  return imageWidth;
}

void wxPGCellRenderer::PostDrawCell( wxDC& dc, const wxPropertyGrid* propGrid, const wxPGCell& cell, int ( flags ) ) const {
  const wxFont& font = cell.GetFont();
  if( font.Ok() ) {
    dc.SetFont( propGrid->GetFont() );
  }
}

void wxPGDefaultRenderer::Render( wxDC& dc, const wxRect& rect, const wxPropertyGrid* propertyGrid, wxPGProperty* property,
                                  int column, int item, int flags ) const {
  bool isUnspecified = property->IsValueUnspecified();
  if( column == 1 && item == -1 ) {
    int cmnVal = property->GetCommonValue();
    if( cmnVal >= 0 ) {
      if( !isUnspecified ) {
        DrawText( dc, rect, 0, propertyGrid->GetCommonValueLabel( cmnVal ) );
      }
      return;
    }
  }
  const wxPGEditor* editor = NULL;
  const wxPGCell* cell = property->GetCell( column );
  wxString text;
  int imageWidth = 0;
  if( column == 1 ) {
    if( !( flags & Control ) ) {
      if( property->IsValueUnspecified() ) {
        cell = &propertyGrid->GetUnspecifiedValueAppearance();
      }
    }
    if( !cell ) {
      const wxPGCell* ccell = property->GetCurrentChoice();
      if( ccell &&
          ( wxGDI_IS_OK( ccell->GetBitmap() ) ||
            wxGDI_IS_OK( ccell->GetFgCol() ) ||
            wxGDI_IS_OK( ccell->GetBgCol() ) )
        ) {
        cell = ccell;
      }
    }
  }
  int preDrawFlags = flags;
  if( cell ) {
    if( propertyGrid->GetInternalFlags() & wxPG_FL_CELL_OVERRIDES_SEL ) {
      preDrawFlags = preDrawFlags & ~( Selected );
    }
    editor = property->GetColumnEditor( column );
    if( editor && !editor->IsKindOf( CLASSINFO( wxPGCheckBoxEditor ) ) ) {
      editor = NULL;
    }
    imageWidth = PreDrawCell( dc, rect, *cell, preDrawFlags );
    text = cell->GetText();
    if( text == gs_noCellText ) {
      if( column == 0 ) {
        text = property->GetLabel();
      } else if( column == 1 ) {
        text = property->GetValueString();
      } else
      { text = wxEmptyString; }
    }
  } else if( column == 0 ) {
    DrawText( dc, rect, 0, property->GetLabel() );
  } else if( column == 1 ) {
    editor = property->GetColumnEditor( column );
    if( !isUnspecified ) {
      wxSize imageSize = propertyGrid->GetImageSize( property, item );
      wxPGPaintData paintdata;
      paintdata.m_parent = propertyGrid;
      paintdata.m_choiceItem = item;
      if( imageSize.x > 0 ) {
        wxRect imageRect( rect.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
                          rect.y + wxPG_CUSTOM_IMAGE_SPACINGY,
                          wxPG_CUSTOM_IMAGE_WIDTH,
                          rect.height - ( wxPG_CUSTOM_IMAGE_SPACINGY * 2 ) );
        dc.SetPen( wxPen( propertyGrid->GetCellTextColour(), 1, wxSOLID ) );
        paintdata.m_drawnWidth = imageSize.x;
        paintdata.m_drawnHeight = imageSize.y;
        if( !isUnspecified ) {
          property->OnCustomPaint( dc, imageRect, paintdata );
        } else {
          dc.SetBrush( *wxWHITE_BRUSH );
          dc.DrawRectangle( imageRect );
        }
        imageWidth = paintdata.m_drawnWidth;
      }
      text = property->GetValueString();
      if( propertyGrid->GetColumnCount() <= 2 ) {
        wxString unitsString = property->GetAttribute( wxPGGlobalVars->m_strUnits, wxEmptyString );
        if( unitsString.length() ) {
          text = wxString::Format( _T( "%s %s" ), text.c_str(), unitsString.c_str() );
        }
      }
    }
    if( text.length() == 0 ) {
      wxVariant vInlineHelp = property->GetAttribute( wxPGGlobalVars->m_strInlineHelp );
      if( !vInlineHelp.IsNull() ) {
        text = vInlineHelp.GetString();
        dc.SetTextForeground( propertyGrid->GetCellDisabledTextColour() );
        editor = NULL;
      }
    }
  } else if( column == 2 ) {
    if( !text.length() ) {
      text = property->GetAttribute( wxPGGlobalVars->m_strUnits, wxEmptyString );
    }
  }
  int imageOffset = property->GetImageOffset( imageWidth );
  DrawEditorValue( dc, rect, imageOffset, text, property, editor );
  if( property->IsCategory() ) {
    if( flags & Selected ) {
      if( imageOffset > 0 ) {
        imageOffset -= DEFAULT_IMAGE_OFFSET_INCREMENT;
        imageOffset += wxCC_CUSTOM_IMAGE_MARGIN2 + 4;
      }
      DrawCaptionSelectionRect( dc, rect.x + wxPG_XBEFORETEXT - wxPG_CAPRECTXMARGIN + imageOffset,
                                rect.y - wxPG_CAPRECTYMARGIN + 1,
                                ( ( wxPropertyCategory* )property )->GetTextExtent( propertyGrid,
                                    propertyGrid->GetCaptionFont() )
                                + ( wxPG_CAPRECTXMARGIN * 2 ),
                                propertyGrid->GetFontHeight() + ( wxPG_CAPRECTYMARGIN * 2 ) );
    }
  }
  if( cell ) {
    PostDrawCell( dc, propertyGrid, *cell, preDrawFlags );
  }
}

wxSize wxPGDefaultRenderer::GetImageSize( const wxPGProperty* property,
    int column,
    int item ) const {
  if( property && column == 1 ) {
    if( item == -1 ) {
      wxBitmap* bmp = property->GetValueImage();
      if( bmp && bmp->Ok() ) {
        return wxSize( bmp->GetWidth(), bmp->GetHeight() );
      }
    }
  }
  return wxSize( 0, 0 );
}

wxPGCell::wxPGCell() {
}

bool wxPGCell::HasText() const {
  if( m_text != gs_noCellText ) {
    return true;
  }
  return false;
}

wxPGCell::wxPGCell( const wxString& text,
                    const wxBitmap& bitmap,
                    const wxColour& fgCol,
                    const wxColour& bgCol )
  : m_bitmap( bitmap ), m_fgCol( fgCol ), m_bgCol( bgCol ) {
  #ifndef __WXPYTHON__
  if( text != wxString_wxPG_LABEL )
  #else
  if( ( &text != ( ( wxString* )NULL ) ) && text != _T( "_LABEL_AS_NAME" ) )
  #endif
  {
    wxASSERT_MSG( m_text != gs_noCellText,
                  _T( "\"@!\" is not an allowed as a cell text." ) );
    m_text = text;
  } else {
    m_text = gs_noCellText;
  }
}

void wxPGCell::Assign( const wxPGCell& cell ) {
  if( cell.HasText() ) {
    SetText( cell.GetText() );
  }
  const wxBitmap& bmp = cell.GetBitmap();
  if( wxGDI_IS_OK( bmp ) ) {
    SetBitmap( bmp );
  }
  const wxColour& fgCol = cell.GetFgCol();
  if( wxGDI_IS_OK( fgCol ) ) {
    SetFgCol( fgCol );
  }
  const wxColour& bgCol = cell.GetBgCol();
  if( wxGDI_IS_OK( bgCol ) ) {
    SetBgCol( bgCol );
  }
  const wxFont& font = cell.GetFont();
  if( wxGDI_IS_OK( font ) ) {
    SetFont( font );
  }
}

class wxPGBrush : public wxBrush {
  public:
    wxPGBrush( const wxColour& colour );
    wxPGBrush();
    virtual ~wxPGBrush() { }
    void SetColour2( const wxColour& colour );
    inline long GetColourAsLong() const { return m_colAsLong; }
  private:
    long    m_colAsLong;
};


void wxPGBrush::SetColour2( const wxColour& colour ) {
  wxBrush::SetColour( colour );
  m_colAsLong = wxPG_COLOUR( colour.Red(), colour.Green(), colour.Blue() );
}


wxPGBrush::wxPGBrush() : wxBrush() {
  m_colAsLong = 0;
}


wxPGBrush::wxPGBrush( const wxColour& colour ) : wxBrush( colour ) {
  m_colAsLong = wxPG_COLOUR( colour.Red(), colour.Green(), colour.Blue() );
}

class wxPGColour : public wxColour {
  public:
    wxPGColour( const wxColour& colour );
    wxPGColour();
    virtual ~wxPGColour() { }
    void SetColour2( const wxColour& colour );
    inline long GetColourAsLong() const { return m_colAsLong; }
  private:
    long    m_colAsLong;
};


void wxPGColour::SetColour2( const wxColour& colour ) {
  *this = colour;
  m_colAsLong = wxPG_COLOUR( colour.Red(), colour.Green(), colour.Blue() );
}


wxPGColour::wxPGColour() : wxColour() {
  m_colAsLong = 0;
}

wxPGColour::wxPGColour( const wxColour& colour ) : wxColour( colour ) {
  m_colAsLong = wxPG_COLOUR( colour.Red(), colour.Green(), colour.Blue() );
}

class wxPGCanvas : public wxPanel {
  public:
    wxPGCanvas() : wxPanel() {
    }
    virtual ~wxPGCanvas() { }

  protected:
    void OnMouseMove( wxMouseEvent &event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnMouseMove( event );
    }

    void OnMouseClick( wxMouseEvent &event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnMouseClick( event );
    }

    void OnMouseUp( wxMouseEvent &event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnMouseUp( event );
    }

    void OnMouseRightClick( wxMouseEvent &event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnMouseRightClick( event );
    }

    void OnMouseDoubleClick( wxMouseEvent &event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnMouseDoubleClick( event );
    }

    void OnKey( wxKeyEvent& event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnKey( event );
      int id = event.GetId();
      event.SetId( pg->GetId() );
      pg->ProcessEvent( event );
      event.SetId( id );
    }

    void OnKeyUp( wxKeyEvent& event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnKeyUp( event );
      int id = event.GetId();
      event.SetId( pg->GetId() );
      pg->ProcessEvent( event );
      event.SetId( id );
    }

    void OnNavigationKey( wxNavigationKeyEvent& event ) {
      wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
      pg->OnNavigationKey( event );
      int id = event.GetId();
      event.SetId( pg->GetId() );
      pg->ProcessEvent( event );
      event.SetId( id );
    }

    void OnPaint( wxPaintEvent& event );

  private:
    DECLARE_EVENT_TABLE()
};


BEGIN_EVENT_TABLE( wxPGCanvas, wxPanel )
  EVT_MOTION( wxPGCanvas::OnMouseMove )
  EVT_PAINT( wxPGCanvas::OnPaint )
  EVT_LEFT_DOWN( wxPGCanvas::OnMouseClick )
  EVT_LEFT_UP( wxPGCanvas::OnMouseUp )
  EVT_RIGHT_UP( wxPGCanvas::OnMouseRightClick )
  EVT_LEFT_DCLICK( wxPGCanvas::OnMouseDoubleClick )
  EVT_KEY_DOWN( wxPGCanvas::OnKey )
  EVT_KEY_UP( wxPGCanvas::OnKeyUp )
  EVT_CHAR( wxPGCanvas::OnKey )
  EVT_NAVIGATION_KEY( wxPGCanvas::OnNavigationKey )
END_EVENT_TABLE()


void wxPGCanvas::OnPaint( wxPaintEvent & ( event ) ) {
  wxPropertyGrid* pg = wxStaticCast( GetParent(), wxPropertyGrid );
  wxASSERT( pg->IsKindOf( CLASSINFO( wxPropertyGrid ) ) );
  wxPaintDC dc( this );
  if( !( pg->GetInternalFlags() & wxPG_FL_INITIALIZED ) ) {
    return;
  }
  wxRect r = GetUpdateRegion().GetBox();
  r.x = 0;
  r.width = GetClientSize().x;
  pg->DrawItems( dc, r.y, r.y + r.height, &r );
  pg->SetInternalFlag( wxPG_FL_GOOD_SIZE_SET );
}

IMPLEMENT_DYNAMIC_CLASS( wxPropertyGrid, wxScrolledWindow )

BEGIN_EVENT_TABLE( wxPropertyGrid, wxScrolledWindow )
  EVT_IDLE( wxPropertyGrid::OnIdle )
  EVT_MOTION( wxPropertyGrid::OnMouseMoveBottom )
  EVT_PAINT( wxPropertyGrid::OnPaint )
  EVT_SIZE( wxPropertyGrid::OnResize )
  EVT_ENTER_WINDOW( wxPropertyGrid::OnMouseEntry )
  EVT_LEAVE_WINDOW( wxPropertyGrid::OnMouseEntry )
  EVT_MOUSE_CAPTURE_CHANGED( wxPropertyGrid::OnCaptureChange )
  EVT_SCROLLWIN( wxPropertyGrid::OnScrollEvent )
  EVT_CHILD_FOCUS( wxPropertyGrid::OnChildFocusEvent )
  EVT_SET_FOCUS( wxPropertyGrid::OnFocusEvent )
  EVT_KILL_FOCUS( wxPropertyGrid::OnFocusEvent )
  EVT_TEXT_ENTER( wxPG_SUBID1, wxPropertyGrid::OnCustomEditorEvent )
  EVT_SYS_COLOUR_CHANGED( wxPropertyGrid::OnSysColourChanged )
END_EVENT_TABLE()

wxPropertyGrid::wxPropertyGrid()
  : wxScrolledWindow() {
  Init1();
}


wxPropertyGrid::wxPropertyGrid( wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
                                long style, const wxString& name )
  : wxScrolledWindow() {
  Init1();
  Create( parent, id, pos, size, style, name );
}

bool wxPropertyGrid::Create( wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
                             long style, const wxString& name ) {
  if( !( style & wxBORDER_MASK ) || ( style & wxBORDER_THEME ) ) {
    style |= wxBORDER_SUNKEN;
  }
  style |= wxVSCROLL;
  if( style & wxTAB_TRAVERSAL ) {
    style |= wxWANTS_CHARS;
  }
  wxScrolledWindow::Create( parent, id, pos, size, style, name );
  Init2();
  return true;
}

void wxPropertyGrid::Init1() {
  #if !wxPG_USE_WXMODULE
  if( !wxPGGlobalVars ) {
    wxPGGlobalVars = new wxPGGlobalVarsClass();
  }
  #endif
  if( wxPGGlobalVars->m_mapEditorClasses.empty() ) {
    RegisterDefaultEditors();
  }
  m_iFlags = 0;
  m_pState = ( wxPropertyGridState* ) NULL;
  m_wndEditor = m_wndEditor2 = ( wxWindow* ) NULL;
  m_selColumn = 1;
  m_colHover = 1;
  m_propHover = ( wxPGProperty* ) NULL;
  m_labelEditor = NULL;
  m_labelEditorProperty = NULL;
  m_eventObject = this;
  m_curFocused = ( wxWindow* ) NULL;
  m_processedEvent = NULL;
  m_sortFunction = NULL;
  m_inDoPropertyChanged = 0;
  m_inCommitChangesFromEditor = 0;
  m_inDoSelectProperty = false;
  m_inOnValidationFailure = false;
  m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT;
  m_dragStatus = 0;
  m_mouseSide = 16;
  m_editorFocused = 0;
  m_unspecifiedAppearance.SetFgCol( *wxLIGHT_GREY );
  AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT );
  AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN );
  AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_LEFT );
  AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_UP );
  AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY, WXK_RIGHT );
  AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY, WXK_LEFT );
  AddActionTrigger( wxPG_ACTION_CANCEL_EDIT, WXK_ESCAPE );
  AddActionTrigger( wxPG_ACTION_SELECT_ALL, 'A', wxMOD_CONTROL );
  m_coloursCustomized = 0;
  m_frozen = 0;
  m_canvas = NULL;
  #if wxPG_DOUBLE_BUFFER
  m_doubleBuffer = ( wxBitmap* ) NULL;
  #endif
  m_windowsToDelete = NULL;
  #ifndef wxPG_ICON_WIDTH
  m_expandbmp = NULL;
  m_collbmp = NULL;
  m_iconWidth = 11;
  m_iconHeight = 11;
  #else
  m_iconWidth = wxPG_ICON_WIDTH;
  #endif
  m_prevVY = -1;
  m_gutterWidth = wxPG_GUTTER_MIN;
  m_subgroup_extramargin = 10;
  m_lineHeight = 0;
  m_width = m_height = 0;
  SetButtonShortcut( 0 );
  m_keyComboConsumed = 0;
  m_commonValues.push_back( new wxPGCommonValue( _T( "Unspecified" ), wxPGGlobalVars->m_defaultRenderer ) );
  m_cvUnspecified = 0;
  m_chgInfo_changedProperty = NULL;
}

void wxPropertyGrid::Init2() {
  wxASSERT( !( m_iFlags & wxPG_FL_INITIALIZED ) );
  if( !m_pState ) {
    m_pState = CreateState();
    m_pState->m_pPropGrid = this;
    m_iFlags |= wxPG_FL_CREATEDSTATE;
  }
  if( !( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) ) {
    m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
  }
  if( m_windowStyle & wxPG_HIDE_CATEGORIES ) {
    m_pState->InitNonCatMode();
    m_pState->m_properties = m_pState->m_abcArray;
  }
  GetClientSize( &m_width, &m_height );
  #ifndef wxPG_ICON_WIDTH
  m_expandbmp = new wxBitmap( expand_xpm );
  m_collbmp = new wxBitmap( collapse_xpm );
  m_iconWidth = m_expandbmp->GetWidth();
  m_iconHeight = m_expandbmp->GetHeight();
  #endif
  m_curcursor = wxCURSOR_ARROW;
  m_cursorSizeWE = new wxCursor( wxCURSOR_SIZEWE );
  m_vspacing = wxPG_DEFAULT_VSPACING;
  CalculateFontAndBitmapStuff( m_vspacing );
  m_arrBgBrushes.Add( ( void* )new wxPGBrush() );
  m_arrFgCols.Add( ( void* )new wxPGColour() );
  m_arrFgCols.Add( ( void* )new wxPGColour() );
  RegainColours();
  SetBackgroundStyle( wxBG_STYLE_CUSTOM );
  m_tlp = NULL;
  m_tlpClosed = NULL;
  m_tlpClosedTime = 0;
  OnTLPChanging( ::wxGetTopLevelParent( this ) );
  wxSize wndsize = GetSize();
  SetVirtualSize( wndsize.GetWidth(), wndsize.GetWidth() );
  m_timeCreated = ::wxGetLocalTimeMillis();
  m_canvas = new wxPGCanvas();
  m_canvas->Create( this, 1, wxPoint( 0, 0 ), GetClientSize(), ( GetWindowStyle() & wxTAB_TRAVERSAL ) |
                    wxWANTS_CHARS | wxCLIP_CHILDREN );
  m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM );
  wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
  sizer->Add( m_canvas, 1, wxEXPAND, 0 );
  SetSizer( sizer );
  SetScrollRate( wxPG_PIXELS_PER_UNIT, wxPG_PIXELS_PER_UNIT );
  m_iFlags |= wxPG_FL_INITIALIZED;
  m_ncWidth = wndsize.GetWidth();
  wxSizeEvent sizeEvent( wndsize, 0 );
  OnResize( sizeEvent );
}

wxPropertyGrid::~wxPropertyGrid() {
  size_t i;
  if( m_processedEvent ) {
    m_processedEvent->Skip( false );
    m_processedEvent->StopPropagation();
    ::wxMessageBox( _T( "wxPropertyGrid 在一个由它产生的事件中被摧毁 这通常会导致崩溃 因此建议在空闲时销毁控件." ) );
  }
  DoSelectProperty( NULL, wxPG_SEL_NOVALIDATE | wxPG_SEL_DONT_SEND_EVENT );
  m_iFlags &= ~( wxPG_FL_INITIALIZED );
  if( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) {
    m_canvas->ReleaseMouse();
  }
  if( !( GetExtraStyle() & wxPG_EX_DISABLE_TLP_TRACKING ) ) {
    OnTLPChanging( NULL );
    #ifdef __WXDEBUG__
    if( IsEditorsValueModified() )
      ::wxMessageBox( _T( "Most recent change in property editor " )
                      _T( "was lost!!!\n\n(if you don't want this " )
                      _T( "to happen, close your frames and " )
                      _T( "dialogs using Close(false).)" ),
                      _T( "wxPropertyGrid Debug Warning" ) );
    #endif
  }
  #if wxPG_DOUBLE_BUFFER
  if( m_doubleBuffer ) {
    delete m_doubleBuffer;
  }
  #endif
  delete m_windowsToDelete;
  if( m_iFlags & wxPG_FL_CREATEDSTATE ) {
    delete m_pState;
  }
  delete m_cursorSizeWE;
  #ifndef wxPG_ICON_WIDTH
  delete m_expandbmp;
  delete m_collbmp;
  #endif
  for( i = 0; i < m_arrFgCols.GetCount(); i++ ) {
    delete( wxPGColour* )m_arrFgCols.Item( i );
  }
  for( i = 0; i < m_arrBgBrushes.GetCount(); i++ ) {
    delete( wxPGBrush* )m_arrBgBrushes.Item( i );
  }
  for( i = 0; i < m_commonValues.size(); i++ ) {
    delete GetCommonValue( i );
  }
}

bool wxPropertyGrid::Destroy() {
  if( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) {
    m_canvas->ReleaseMouse();
  }
  return wxScrolledWindow::Destroy();
}

wxPropertyGridState* wxPropertyGrid::CreateState() const {
  return new wxPropertyGridState();
}

void wxPropertyGrid::SetWindowStyleFlag( long style ) {
  long old_style = m_windowStyle;
  if( m_iFlags & wxPG_FL_INITIALIZED ) {
    wxASSERT( m_pState );
    if( !( style & wxPG_HIDE_CATEGORIES ) && ( old_style & wxPG_HIDE_CATEGORIES ) ) {
      EnableCategories( true );
    } else if( ( style & wxPG_HIDE_CATEGORIES ) && !( old_style & wxPG_HIDE_CATEGORIES ) ) {
      EnableCategories( false );
    }
    if( !( old_style & wxPG_AUTO_SORT ) && ( style & wxPG_AUTO_SORT ) ) {
      if( !m_frozen ) {
        PrepareAfterItemsAdded();
      } else
      { m_pState->m_itemsAdded = 1; }
    }
    #if wxPG_SUPPORT_TOOLTIPS
    if( !( old_style & wxPG_TOOLTIPS ) && ( style & wxPG_TOOLTIPS ) ) {
    } else if( ( old_style & wxPG_TOOLTIPS ) && !( style & wxPG_TOOLTIPS ) ) {
      m_canvas->SetToolTip( ( wxToolTip* ) NULL );
    }
    #endif
  }
  wxScrolledWindow::SetWindowStyleFlag( style );
  if( m_iFlags & wxPG_FL_INITIALIZED ) {
    if( ( old_style & wxPG_HIDE_MARGIN ) != ( style & wxPG_HIDE_MARGIN ) ) {
      CalculateFontAndBitmapStuff( m_vspacing );
      Refresh();
    }
  }
}


void wxPropertyGrid::Freeze() {
  m_frozen++;
  wxScrolledWindow::Freeze();
}


void wxPropertyGrid::Thaw() {
  m_frozen--;
  wxScrolledWindow::Thaw();
  RecalculateVirtualSize();
  #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
  m_canvas->Refresh();
  #endif
  wxArrayPGProperty selection = m_pState->m_selection;
  DoSetSelection( selection, wxPG_SEL_FORCE | wxPG_SEL_NONVISIBLE );
}


bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags ) {
  wxCHECK( prop, false );
  if( !( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION ) ) {
    return DoSelectProperty( prop, selFlags );
  }
  wxArrayPGProperty& selection = m_pState->m_selection;
  if( !selection.size() ) {
    return DoSelectProperty( prop, selFlags );
  } else {
    if( prop->IsCategory() || selection[0]->IsCategory() ) {
      return true;
    }
    selection.push_back( prop );
    {
      SendEvent( wxEVT_PG_SELECTED, prop, NULL );
    }
    DrawItem( prop );
  }
  return true;
}


bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags ) {
  wxCHECK( prop, false );
  bool res;
  wxArrayPGProperty& selection = m_pState->m_selection;
  if( selection.size() <= 1 ) {
    res = DoSelectProperty( NULL, selFlags );
  } else {
    m_pState->DoRemoveFromSelection( prop );
    DrawItem( prop );
    res = true;
  }
  return res;
}


bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty* prop, unsigned int colIndex, unsigned int selFlags ) {
  bool res;
  if( colIndex == 1 ) {
    res = DoSelectProperty( prop, selFlags );
  } else {
    DoClearSelection( false, wxPG_SEL_NO_REFRESH );
    if( m_pState->m_editableColumns.Index( colIndex ) == wxNOT_FOUND ) {
      res = DoAddToSelection( prop, selFlags );
    } else {
      res = DoAddToSelection( prop, selFlags | wxPG_SEL_NO_REFRESH );
      DoBeginLabelEdit( colIndex, selFlags );
    }
  }
  return res;
}


bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop,
    unsigned int colIndex,
    wxMouseEvent* mouseEvent,
    int selFlags ) {
  const wxArrayPGProperty& selection = GetSelectedProperties();
  bool alreadySelected = m_pState->DoIsPropertySelected( prop );
  bool res = true;
  int addToExistingSelection = 0;
  if( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION ) {
    if( mouseEvent ) {
      if( mouseEvent->GetEventType() == wxEVT_RIGHT_DOWN ||
          mouseEvent->GetEventType() == wxEVT_RIGHT_UP ) {
        if( GetSelectedProperties().size() <= 1 ||
            !alreadySelected ) {
          return DoSelectAndEdit( prop, colIndex, selFlags );
        }
        return true;
      } else {
        if( mouseEvent->ControlDown() ) {
          addToExistingSelection = 1;
        } else if( mouseEvent->ShiftDown() ) {
          if( selection.size() > 0 && !prop->IsCategory() ) {
            addToExistingSelection = 2;
          } else
          { addToExistingSelection = 1; }
        }
      }
    }
  }
  if( addToExistingSelection == 1 ) {
    if( !alreadySelected ) {
      res = DoAddToSelection( prop, selFlags );
    } else if( GetSelectedProperties().size() > 1 ) {
      res = DoRemoveFromSelection( prop, selFlags );
    }
  } else if( addToExistingSelection == 2 ) {
    wxPGProperty* topSelProp = selection[0];
    int topSelPropY = topSelProp->GetY();
    for( unsigned int i = 1; i < selection.size(); i++ ) {
      wxPGProperty* p = selection[i];
      int y = p->GetY();
      if( y < topSelPropY ) {
        topSelProp = p;
        topSelPropY = y;
      }
    }
    wxPGProperty* startFrom;
    wxPGProperty* stopAt;
    if( prop->GetY() <= topSelPropY ) {
      startFrom = prop;
      stopAt = topSelProp;
    } else {
      startFrom = topSelProp;
      stopAt = prop;
    }
    wxPropertyGridIterator it;
    for( it = GetIterator( wxPG_ITERATE_VISIBLE, startFrom );
         !it.AtEnd();
         it++ ) {
      wxPGProperty* p = *it;
      if( !p->IsCategory() &&
          !m_pState->DoIsPropertySelected( p ) ) {
        DoAddToSelection( p, selFlags );
      }
      if( p == stopAt ) {
        break;
      }
    }
  } else {
    res = DoSelectAndEdit( prop, colIndex, selFlags );
  }
  return res;
}


void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty& newSelection, int selFlags ) {
  if( newSelection.size() > 0 ) {
    if( !DoSelectProperty( newSelection[0], selFlags ) ) {
      return;
    }
  } else {
    DoClearSelection( false, selFlags );
  }
  for( unsigned int i = 1; i < newSelection.size(); i++ ) {
    DoAddToSelection( newSelection[i], selFlags );
  }
  Refresh();
}


void wxPropertyGrid::MakeColumnEditable( unsigned int column,
    bool editable ) {
  wxASSERT( column != 1 );
  wxArrayInt& cols = m_pState->m_editableColumns;
  if( editable ) {
    cols.push_back( column );
  } else {
    for( int i = cols.size() - 1; i > 0; i-- ) {
      if( cols[i] == ( int )column ) {
        cols.erase( cols.begin() + i );
      }
    }
  }
}


void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex,
                                       int selFlags ) {
  wxPGProperty* selected = GetSelection();
  wxCHECK_RET( selected, _T( "No property selected" ) );
  wxCHECK_RET( colIndex != 1, _T( "Do not use this for column 1" ) );
  if( !( selFlags & wxPG_SEL_DONT_SEND_EVENT ) ) {
    if( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN,
                   selected, NULL, 0,
                   colIndex ) ) {
      return;
    }
  }
  wxString text;
  wxPGCell* cell = selected->GetCell( colIndex );
  if( !cell ) {
    if( colIndex == 0 ) {
      text = selected->GetLabel();
    } else
    { cell = selected->GetOrCreateCell( colIndex ); }
  }
  if( cell ) {
    text = cell->GetText();
  }
  DoEndLabelEdit( true, wxPG_SEL_NOVALIDATE );
  m_selColumn = colIndex;
  wxRect r = GetEditorWidgetRect( selected, m_selColumn );
  wxWindow* ed = GenerateEditorTextCtrl( r.GetPosition(),
                                         r.GetSize(),
                                         text,
                                         NULL,
                                         wxTE_PROCESS_ENTER,
                                         0,
                                         colIndex );
  m_labelEditor = ed;
  wxTextCtrl* tc = GetLabelEditor();
  wxWindowID id = tc->GetId();
  tc->Connect( id, wxEVT_COMMAND_TEXT_ENTER,
               wxCommandEventHandler( wxPropertyGrid::OnLabelEditorEnterPress ),
               NULL, this );
  tc->Connect( id, wxEVT_KEY_DOWN,
               wxKeyEventHandler( wxPropertyGrid::OnLabelEditorKeyPress ),
               NULL, this );
  tc->SetFocus();
  m_labelEditorProperty = selected;
  DrawItem( selected );
}


void
wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent & ( event ) ) {
  DoEndLabelEdit( true );
}


void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event ) {
  int keycode = event.GetKeyCode();
  if( keycode == WXK_ESCAPE ) {
    DoEndLabelEdit( false );
  } else {
    int secondAction;
    if( KeyEventToActions( event, &secondAction ) != wxPG_ACTION_INVALID &&
        keycode != WXK_LEFT && keycode != WXK_RIGHT ) {
      HandleKeyEvent( event );
    } else
    { event.Skip(); }
  }
}


void wxPropertyGrid::DoEndLabelEdit( bool commit, int selFlags ) {
  if( !m_labelEditor ) {
    return;
  }
  wxPGProperty* prop = m_labelEditorProperty;
  wxASSERT( prop );
  if( commit ) {
    if( !( selFlags & wxPG_SEL_DONT_SEND_EVENT ) ) {
      if( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING,
                     prop, NULL, selFlags,
                     m_selColumn ) ) {
        return;
      }
    }
    wxString text = GetLabelEditor()->GetValue();
    wxPGCell* cell = prop->GetCell( m_selColumn );
    if( !cell ) {
      if( m_selColumn == 0 ) {
        prop->SetLabel( text );
      } else
      { cell = prop->GetOrCreateCell( m_selColumn ); }
    }
    if( cell ) {
      cell->SetText( text );
    }
  }
  m_selColumn = 1;
  int wasFocused = m_iFlags & wxPG_FL_FOCUSED;
  DestroyEditorWnd( m_labelEditor );
  m_labelEditor = NULL;
  m_labelEditorProperty = NULL;
  if( wasFocused ) {
    SetFocusOnCanvas();
  }
  DrawItem( prop );
}


void wxPropertyGrid::SetExtraStyle( long exStyle ) {
  if( exStyle & wxPG_EX_DISABLE_TLP_TRACKING ) {
    OnTLPChanging( NULL );
  } else {
    OnTLPChanging( ::wxGetTopLevelParent( this ) );
  }
  if( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING ) {
    if( !wxPGIsWindowBuffered( this ) ) {
      exStyle &= ~( wxPG_EX_NATIVE_DOUBLE_BUFFERING );
    } else {
      #if wxPG_DOUBLE_BUFFER
      delete m_doubleBuffer;
      m_doubleBuffer = NULL;
      #endif
    }
  }
  wxScrolledWindow::SetExtraStyle( exStyle );
  if( exStyle & wxPG_EX_INIT_NOCAT ) {
    m_pState->InitNonCatMode();
  }
  if( exStyle & wxPG_EX_HELP_AS_TOOLTIPS ) {
    m_windowStyle |= wxPG_TOOLTIPS;
  }
  wxPGGlobalVars->m_extraStyle = exStyle;
}



wxSize wxPropertyGrid::DoGetBestSize() const {
  int hei = 15;
  if( m_lineHeight > hei ) {
    hei = m_lineHeight;
  }
  wxSize sz = wxSize( 60, hei + 40 );
  CacheBestSize( sz );
  return sz;
}


void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP ) {
  if( newTLP == m_tlp ) {
    return;
  }
  wxLongLong currentTime = ::wxGetLocalTimeMillis();
  if( m_tlp ) {
    m_tlp->Disconnect( wxEVT_CLOSE_WINDOW,
                       wxCloseEventHandler( wxPropertyGrid::OnTLPClose ),
                       NULL, this );
    m_tlpClosed = m_tlp;
    m_tlpClosedTime = currentTime;
  }
  if( newTLP ) {
    if( newTLP != m_tlpClosed ||
        m_tlpClosedTime + 250 < currentTime ) {
      newTLP->Connect( wxEVT_CLOSE_WINDOW,
                       wxCloseEventHandler( wxPropertyGrid::OnTLPClose ),
                       NULL, this );
      m_tlpClosed = NULL;
    } else
    { newTLP = NULL; }
  }
  m_tlp = newTLP;
}


void wxPropertyGrid::OnTLPClose( wxCloseEvent& event ) {
  if( event.CanVeto() && !ClearSelection() ) {
    event.Veto();
    return;
  }
  OnTLPChanging( NULL );
  event.Skip();
}


bool wxPropertyGrid::Reparent( wxWindowBase *newParent ) {
  OnTLPChanging( ( wxWindow* )newParent );
  bool res = wxScrolledWindow::Reparent( newParent );
  return res;
}



void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing ) {
  int x = 0, y = 0;
  m_captionFont = GetFont();
  GetTextExtent( _T( "jG" ), &x, &y, 0, 0, &m_captionFont );
  m_subgroup_extramargin = x + ( x / 2 );
  m_fontHeight = y;
  #if wxPG_USE_RENDERER_NATIVE
  m_iconWidth = wxPG_ICON_WIDTH;
  #elif wxPG_ICON_WIDTH
  m_iconWidth = ( m_fontHeight * wxPG_ICON_WIDTH ) / 13;
  if( m_iconWidth < 5 ) {
    m_iconWidth = 5;
  } else if( !( m_iconWidth & 0x01 ) ) {
    m_iconWidth++;
  }
  #endif
  m_gutterWidth = m_iconWidth / wxPG_GUTTER_DIV;
  if( m_gutterWidth < wxPG_GUTTER_MIN ) {
    m_gutterWidth = wxPG_GUTTER_MIN;
  }
  int vdiv = 6;
  if( vspacing <= 1 ) {
    vdiv = 12;
  } else if( vspacing >= 3 ) {
    vdiv = 3;
  }
  m_spacingy = m_fontHeight / vdiv;
  if( m_spacingy < wxPG_YSPACING_MIN ) {
    m_spacingy = wxPG_YSPACING_MIN;
  }
  m_marginWidth = 0;
  if( !( m_windowStyle & wxPG_HIDE_MARGIN ) ) {
    m_marginWidth = m_gutterWidth * 2 + m_iconWidth;
  }
  m_captionFont.SetWeight( wxBOLD );
  GetTextExtent( _T( "jG" ), &x, &y, 0, 0, &m_captionFont );
  m_lineHeight = m_fontHeight + ( 2 * m_spacingy ) + 1;
  m_visPropArray.SetCount( ( m_height / m_lineHeight ) + 10 );
  m_buttonSpacingY = ( m_lineHeight - m_iconHeight ) / 2;
  if( m_buttonSpacingY < 0 ) {
    m_buttonSpacingY = 0;
  }
  if( m_pState ) {
    m_pState->CalculateFontAndBitmapStuff( vspacing );
  }
  if( m_iFlags & wxPG_FL_INITIALIZED ) {
    RecalculateVirtualSize();
  }
  InvalidateBestSize();
}


void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent & ( event ) ) {
  RegainColours();
  Refresh();
}


static wxColour wxPGAdjustColour( const wxColour& src, int ra,
                                  int ga = 1000, int ba = 1000,
                                  bool forceDifferent = false ) {
  if( ga >= 1000 ) {
    ga = ra;
  }
  if( ba >= 1000 ) {
    ba = ra;
  }
  static int isinside = 0;
  isinside++;
  wxCHECK_MSG( isinside < 3,
               *wxBLACK,
               _T( "wxPGAdjustColour should not be recursively called more than once" ) );
  wxColour dst;
  int r = src.Red();
  int g = src.Green();
  int b = src.Blue();
  int r2 = r + ra;
  if( r2 > 255 ) {
    r2 = 255;
  } else if( r2 < 0 ) {
    r2 = 0;
  }
  int g2 = g + ga;
  if( g2 > 255 ) {
    g2 = 255;
  } else if( g2 < 0 ) {
    g2 = 0;
  }
  int b2 = b + ba;
  if( b2 > 255 ) {
    b2 = 255;
  } else if( b2 < 0 ) {
    b2 = 0;
  }
  if( forceDifferent && ( abs( ( r + g + b ) - ( r2 + g2 + b2 ) ) < abs( ra / 2 ) ) ) {
    dst = wxPGAdjustColour( src, -( ra * 2 ) );
  } else {
    dst = wxColour( r2, g2, b2 );
  }
  isinside--;
  return dst;
}


static int wxPGGetColAvg( const wxColour& col ) {
  return ( col.Red() + col.Green() + col.Blue() ) / 3;
}


void wxPropertyGrid::RegainColours() {
  wxColour def_bgcol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
  if( !( m_coloursCustomized & 0x0002 ) ) {
    wxColour col = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
    int colDec = wxPGGetColAvg( col ) - 230;
    if( colDec > 0 ) {
      m_colCapBack = wxPGAdjustColour( col, -colDec );
    } else
    { m_colCapBack = col; }
  }
  if( !( m_coloursCustomized & 0x0001 ) ) {
    m_colMargin = m_colCapBack;
  }
  if( !( m_coloursCustomized & 0x0004 ) ) {
    int colDec = -90;
    wxColour capForeCol = wxPGAdjustColour( m_colCapBack, colDec, 5000, 5000, true );
    m_colCapFore = capForeCol;
    ( ( wxPGColour* )m_arrFgCols.Item( 1 ) )->SetColour2( capForeCol );
  }
  if( !( m_coloursCustomized & 0x0008 ) ) {
    wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
    m_colPropBack = bgCol;
    ( ( wxPGBrush* )m_arrBgBrushes.Item( 0 ) )->SetColour2( bgCol );
  }
  if( !( m_coloursCustomized & 0x0010 ) ) {
    wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
    m_colPropFore = fgCol;
    ( ( wxPGColour* )m_arrFgCols.Item( 0 ) )->SetColour2( fgCol );
  }
  if( !( m_coloursCustomized & 0x0020 ) ) {
    m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
  }
  if( !( m_coloursCustomized & 0x0040 ) ) {
    m_colSelFore = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
  }
  if( !( m_coloursCustomized & 0x0080 ) ) {
    m_colLine = m_colCapBack;
  }
  if( !( m_coloursCustomized & 0x0100 ) ) {
    m_colDisPropFore = m_colCapFore;
  }
  m_colEmptySpace = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
}


void wxPropertyGrid::ResetColours() {
  m_coloursCustomized = 0;
  RegainColours();
  Refresh();
}


bool wxPropertyGrid::SetFont( const wxFont& font ) {
  ClearSelection( false );
  bool res = wxScrolledWindow::SetFont( font );
  if( res && GetParent() ) {
    CalculateFontAndBitmapStuff( m_vspacing );
    if( m_pState ) {
      m_pState->CalculateFontAndBitmapStuff( m_vspacing );
    }
    Refresh();
  }
  return res;
}


void wxPropertyGrid::SetLineColour( const wxColour& col ) {
  m_colLine = col;
  m_coloursCustomized |= 0x80;
  Refresh();
}


void wxPropertyGrid::SetMarginColour( const wxColour& col ) {
  m_colMargin = col;
  m_coloursCustomized |= 0x01;
  Refresh();
}


void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col ) {
  m_colPropBack = col;
  m_coloursCustomized |= 0x08;
  ( ( wxPGBrush* )m_arrBgBrushes.Item( 0 ) )->SetColour2( col );
  Refresh();
}


void wxPropertyGrid::SetCellTextColour( const wxColour& col ) {
  m_colPropFore = col;
  m_coloursCustomized |= 0x10;
  ( ( wxPGColour* )m_arrFgCols.Item( 0 ) )->SetColour2( col );
  Refresh();
}


void wxPropertyGrid::SetEmptySpaceColour( const wxColour& col ) {
  m_colEmptySpace = col;
  Refresh();
}


void wxPropertyGrid::SetCellDisabledTextColour( const wxColour& col ) {
  m_colDisPropFore = col;
  m_coloursCustomized |= 0x100;
  Refresh();
}


void wxPropertyGrid::SetSelectionBackground( const wxColour& col ) {
  m_colSelBack = col;
  m_coloursCustomized |= 0x20;
  Refresh();
}


void wxPropertyGrid::SetSelectionForeground( const wxColour& col ) {
  m_colSelFore = col;
  m_coloursCustomized |= 0x40;
  Refresh();
}


void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour& col ) {
  m_colCapBack = col;
  m_coloursCustomized |= 0x02;
  Refresh();
}


void wxPropertyGrid::SetCaptionForegroundColour( const wxColour& col ) {
  m_colCapFore = col;
  m_coloursCustomized |= 0x04;
  ( ( wxPGColour* )m_arrFgCols.Item( 1 ) )->SetColour2( col );
  Refresh();
}


void wxPropertyGrid::SetBackgroundColourIndex( wxPGProperty* p,
    int index,
    int flags ) {
  unsigned char ind = index;
  p->m_bgColIndex = ind;
  if( flags & wxPG_RECURSE ) {
    unsigned int i;
    for( i = 0; i < p->GetChildCount(); i++ ) {
      SetBackgroundColourIndex( p->Item( i ), index );
    }
  }
}


void wxPropertyGrid::SetPropertyBackgroundColour( wxPGPropArg id,
    const wxColour& colour,
    int flags ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  size_t i;
  int colInd = -1;
  long colAsLong = wxPG_COLOUR( colour.Red(), colour.Green(), colour.Blue() );
  for( i = ( m_arrBgBrushes.GetCount() - 1 ); i > 0; i-- ) {
    if( ( ( wxPGBrush* )m_arrBgBrushes.Item( i ) )->GetColourAsLong() ==
        colAsLong ) {
      colInd = i;
      break;
    }
  }
  if( colInd < 0 ) {
    colInd = m_arrBgBrushes.GetCount();
    wxCHECK_RET( colInd < 256,
                 _T( "wxPropertyGrid: Warning - Only 255 different " )
                 _T( "property background colours allowed." ) );
    m_arrBgBrushes.Add( ( void* )new wxPGBrush( colour ) );
  }
  SetBackgroundColourIndex( p, colInd, flags );
  DrawItemAndChildren( p );
}


wxColour wxPropertyGrid::GetPropertyBackgroundColour( wxPGPropArg id ) const {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxColour() )
  if( p->IsCategory() && p->m_bgColIndex == 0 ) {
    return GetCaptionBackgroundColour();
  }
  return ( ( wxPGBrush* )m_arrBgBrushes.Item( p->m_bgColIndex ) )->GetColour();
}


void wxPropertyGrid::SetTextColourIndex( wxPGProperty* p, int index,
    int flags ) {
  unsigned char ind = index;
  p->m_fgColIndex = ind;
  if( flags & wxPG_RECURSE ) {
    unsigned int i;
    for( i = 0; i < p->GetChildCount(); i++ ) {
      SetTextColourIndex( p->Item( i ), index, flags );
    }
  }
}


int wxPropertyGrid::CacheColour( const wxColour& colour ) {
  unsigned int i;
  int colInd = -1;
  long colAsLong = wxPG_COLOUR( colour.Red(), colour.Green(), colour.Blue() );
  for( i = ( m_arrFgCols.GetCount() - 1 ); i > 0; i-- ) {
    if( ( ( wxPGColour* )m_arrFgCols.Item( i ) )->GetColourAsLong() ==
        colAsLong ) {
      colInd = i;
      break;
    }
  }
  if( colInd < 0 ) {
    colInd = m_arrFgCols.GetCount();
    wxCHECK_MSG( colInd < 256, 0,
                 _T( "wxPropertyGrid: Warning - Only 255 " )
                 _T( "different property foreground colours allowed." ) );
    m_arrFgCols.Add( ( void* )new wxPGColour( colour ) );
  }
  return colInd;
}


void wxPropertyGrid::SetPropertyTextColour( wxPGPropArg id,
    const wxColour& colour,
    int flags ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  SetTextColourIndex( p, CacheColour( colour ), flags );
  DrawItemAndChildren( p );
}


void wxPropertyGrid::SetCaptionTextColour( wxPGPropArg id, const wxColour& colour ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  wxCHECK_RET( p->IsCategory(),
               _T( "Only call SetCaptionTextColour for caption properties" ) );
  wxPropertyCategory* cat = ( wxPropertyCategory* ) p;
  cat->SetTextColIndex( CacheColour( colour ) );
  DrawItemAndChildren( p );
}


wxColour wxPropertyGrid::GetPropertyTextColour( wxPGPropArg id ) const {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxColour() )
  return wxColour( *( ( wxPGColour* )m_arrFgCols.Item( p->m_fgColIndex ) ) );
}


void wxPropertyGrid::SetPropertyColourToDefault( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  SetBackgroundColourIndex( p, 0 );
  SetTextColourIndex( p, 0, wxPG_RECURSE );
  if( p->IsCategory() ) {
    wxPropertyCategory* cat = ( wxPropertyCategory* ) p;
    cat->SetTextColIndex( 1 );
  }
}



void wxPropertyGridInterface::RefreshGrid( wxPropertyGridState* state ) {
  if( !state ) {
    state = m_pState;
  }
  wxPropertyGrid* grid = state->GetGrid();
  if( grid->GetState() == state && !grid->IsFrozen() ) {
    grid->Refresh();
  }
}


wxPGProperty* wxPropertyGridInterface::Append( wxPGProperty* property ) {
  return m_pState->DoAppend( property );
}


wxPGProperty* wxPropertyGridInterface::AppendIn( wxPGPropArg id, wxPGProperty* newproperty ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxNullProperty )
  wxPGProperty* pwc = ( wxPGProperty* ) p;
  wxPGProperty* retp = m_pState->DoInsert( pwc, pwc->GetChildCount(), newproperty );
  return retp;
}


wxPGProperty* wxPropertyGridInterface::Insert( wxPGPropArg id, wxPGProperty* property ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxNullProperty )
  wxPGProperty* retp = m_pState->DoInsert( p->GetParent(), p->GetArrIndex(), property );
  RefreshGrid();
  return retp;
}


wxPGProperty* wxPropertyGridInterface::Insert( wxPGPropArg id, int index, wxPGProperty* newproperty ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxNullProperty )
  wxPGProperty* retp = m_pState->DoInsert( ( wxPGProperty* )p, index, newproperty );
  RefreshGrid();
  return retp;
}


void wxPropertyGridInterface::DeleteProperty( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  wxPropertyGridState* state = p->GetParentState();
  state->DoDelete( p, true );
  RefreshGrid( state );
}


wxPGProperty* wxPropertyGridInterface::RemoveProperty( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxNullProperty )
  wxCHECK( !p->GetChildCount() || p->HasFlag( wxPG_PROP_AGGREGATE ),
           wxNullProperty );
  wxPropertyGridState* state = p->GetParentState();
  state->DoDelete( p, false );
  RefreshGrid( state );
  return p;
}


wxPGProperty* wxPropertyGridInterface::ReplaceProperty( wxPGPropArg id, wxPGProperty* property ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxNullProperty )
  wxPGProperty* replaced = p;
  wxCHECK_MSG( replaced && property,
               wxNullProperty,
               _T( "NULL property" ) );
  wxCHECK_MSG( !replaced->IsCategory(),
               wxNullProperty,
               _T( "cannot replace this type of property" ) );
  wxCHECK_MSG( !m_pState->IsInNonCatMode(),
               wxNullProperty,
               _T( "cannot replace properties in alphabetic mode" ) );
  wxPGProperty* parent = replaced->GetParent();
  int ind = replaced->GetIndexInParent();
  wxPropertyGridState* state = replaced->GetParentState();
  DeleteProperty( replaced );
  state->DoInsert( parent, ind, property );
  return property;
}


void wxPropertyGrid::PrepareAfterItemsAdded() {
  if( m_pState && m_pState->m_itemsAdded ) {
    m_pState->m_itemsAdded = 0;
    if( m_windowStyle & wxPG_AUTO_SORT ) {
      Sort();
    }
  }
  RecalculateVirtualSize();
}



wxPGProperty* wxPropertyGridInterface::GetSelection() const {
  return m_pState->GetSelection();
}


bool wxPropertyGridInterface::ClearSelection( bool validation ) {
  bool res = DoClearSelection( validation, wxPG_SEL_DONT_SEND_EVENT );
  wxPropertyGrid* pg = GetPropertyGrid();
  if( pg ) {
    pg->Refresh();
  }
  return res;
}


bool wxPropertyGridInterface::DoClearSelection( bool validation,
    int selFlags ) {
  if( !validation ) {
    selFlags |= wxPG_SEL_NOVALIDATE;
  }
  wxPropertyGridState* state = m_pState;
  if( state ) {
    wxPropertyGrid* pg = state->GetGrid();
    if( pg->GetState() == state ) {
      return pg->DoSelectProperty( NULL, selFlags );
    } else
    { state->DoSetSelection( NULL ); }
  }
  return true;
}


bool wxPropertyGridInterface::IsPropertySelected( wxPGPropArg id ) const {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  return m_pState->DoIsPropertySelected( p );
}


void wxPropertyGridInterface::LimitPropertyEditing( wxPGPropArg id, bool limit ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  m_pState->DoLimitPropertyEditing( p, limit );
  RefreshProperty( p );
}


bool wxPropertyGridInterface::EnableProperty( wxPGPropArg id, bool enable ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  wxPropertyGridState* state = p->GetParentState();
  wxPropertyGrid* grid = state->GetGrid();
  if( enable ) {
    if( !( p->m_flags & wxPG_PROP_DISABLED ) ) {
      return false;
    }
    if( grid && grid->GetState() == state && p == grid->GetSelection() ) {
      grid->DoSelectProperty( p, wxPG_SEL_FORCE );
    }
  } else {
    if( p->m_flags & wxPG_PROP_DISABLED ) {
      return false;
    }
    if( grid && grid->GetState() == state && p == grid->GetSelection() ) {
      grid->DoSelectProperty( p, wxPG_SEL_FORCE );
    }
  }
  state->DoEnableProperty( p, enable );
  RefreshProperty( p );
  return true;
}


bool wxPropertyGridInterface::ExpandAll( bool doExpand ) {
  wxPropertyGridState* state = m_pState;
  if( !state->DoGetRoot()->GetChildCount() ) {
    return true;
  }
  wxPropertyGrid* pg = state->GetGrid();
  if( GetSelection() && GetSelection() != state->DoGetRoot() &&
      !doExpand ) {
    pg->ClearSelection( false );
  }
  wxPGVIterator it;
  for( it = GetVIterator( wxPG_ITERATE_ALL ); !it.AtEnd(); it.Next() ) {
    wxPGProperty* p = ( wxPGProperty* ) it.GetProperty();
    if( p->GetChildCount() ) {
      if( doExpand ) {
        if( !p->IsExpanded() ) {
          state->DoExpand( p );
        }
      } else {
        if( p->IsExpanded() ) {
          state->DoCollapse( p );
        }
      }
    }
  }
  pg->RecalculateVirtualSize();
  RefreshGrid();
  return true;
}



void wxPGGetFailed( const wxPGProperty* p, const wxChar* typestr ) {
  wxPGTypeOperationFailed( p, typestr, _T( "Get" ) );
}


void wxPGTypeOperationFailed( const wxPGProperty* p, const wxChar* typestr,
                              const wxChar* op ) {
  wxASSERT( p != NULL );
  wxLogError( _T( "Type operation \"%s\" failed: Property labeled \"%s\" is of type \"%s\", NOT \"%s\"." ),
              op, p->GetLabel().c_str(), p->GetValue().GetType().c_str(), typestr );
}


void wxPropertyGridInterface::SetPropVal( wxPGPropArg id, wxVariant& value ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  if( p ) {
    p->SetValue( value );
    wxPropertyGrid* propGrid = p->GetGridIfDisplayed();
    if( propGrid ) {
      propGrid->DrawItemAndValueRelated( p );
    }
  }
}


void wxPropertyGridInterface::SetPropertyValueString( wxPGPropArg id, const wxString& value ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  m_pState->DoSetPropertyValueString( p, value );
}



void wxPropertyGrid::DoSetPropertyName( wxPGProperty* p, const wxString& newname ) {
  wxCHECK_RET( p, _T( "invalid property id" ) );
  wxPGProperty* parent = p->GetParent();
  if( parent->IsCategory() || parent->IsRoot() ) {
    if( p->GetBaseName().length() ) {
      m_pState->m_dictName.erase( wxPGNameConv( p->GetBaseName() ) );
    }
    if( newname.length() ) {
      m_pState->m_dictName[newname] = ( void* ) p;
    }
  }
  p->DoSetName( newname );
}


bool wxPropertyGrid::EnsureVisible( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  Update();
  bool changed = false;
  if( !p->IsVisible() ) {
    wxPGProperty* parent = p->GetParent();
    wxPGProperty* grandparent = parent->GetParent();
    if( grandparent && grandparent != m_pState->m_properties ) {
      Expand( grandparent );
    }
    Expand( parent );
    changed = true;
  }
  int vx, vy;
  GetViewStart( &vx, &vy );
  vy *= wxPG_PIXELS_PER_UNIT;
  int y = p->GetY();
  if( y < vy ) {
    Scroll( vx, y / wxPG_PIXELS_PER_UNIT );
    m_iFlags |= wxPG_FL_SCROLLED;
    changed = true;
  } else if( ( y + m_lineHeight ) > ( vy + m_height ) ) {
    Scroll( vx, ( y - m_height + ( m_lineHeight * 2 ) ) / wxPG_PIXELS_PER_UNIT );
    m_iFlags |= wxPG_FL_SCROLLED;
    changed = true;
  }
  if( changed ) {
    DrawItems( p, p );
  }
  return changed;
}



void wxPropertyGrid::SetCurControlBoldFont() {
  wxASSERT( m_wndEditor );
  m_wndEditor->SetFont( m_captionFont );
}


wxPoint wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty* p, const wxSize& sz ) {
  #if wxPG_SMALL_SCREEN
  return wxDefaultPosition;
  #else
  int splitterX = GetSplitterPosition();
  int x = splitterX;
  int y = p->GetY();
  wxCHECK_MSG( y >= 0, wxPoint( -1, -1 ), _T( "invalid y?" ) );
  ImprovedClientToScreen( &x, &y );
  int sw = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X );
  int sh = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y );
  int new_x;
  int new_y;
  if( x > ( sw / 2 ) ) {
    new_x = x + ( m_width - splitterX ) - sz.x;
  } else {
    new_x = x;
  }
  if( y > ( sh / 2 ) ) {
    new_y = y - sz.y;
  } else {
    new_y = y + m_lineHeight;
  }
  return wxPoint( new_x, new_y );
  #endif
}


wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str ) {
  if( src_str.length() == 0 ) {
    dst_str = src_str;
    return src_str;
  }
  bool prev_is_slash = false;
  wxString::const_iterator i = src_str.begin();
  dst_str.clear();
  for( ; i != src_str.end(); i++ ) {
    wxUniChar a = wxPGGetIterChar( src_str, i );
    if( a != _T( '\\' ) ) {
      if( !prev_is_slash ) {
        dst_str << a;
      } else {
        if( a == _T( 'n' ) ) {
          dst_str << _T( '\n' );
        } else if( a == _T( 't' ) ) {
          dst_str << _T( '\t' );
        } else
        { dst_str << a; }
      }
      prev_is_slash = false;
    } else {
      if( prev_is_slash ) {
        dst_str << _T( '\\' );
        prev_is_slash = false;
      } else
      { prev_is_slash = true; }
    }
  }
  return dst_str;
}


wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str ) {
  if( src_str.length() == 0 ) {
    dst_str = src_str;
    return src_str;
  }
  wxString::const_iterator i = src_str.begin();
  dst_str.clear();
  for( ; i != src_str.end(); i++ ) {
    wxChar a = wxPGGetIterChar( src_str, i );
    if( a >= _T( ' ' ) ) {
      dst_str << a;
    } else {
      if( a == _T( '\r' ) ) {
      } else if( a == _T( '\n' ) ) {
        dst_str << _T( "\\n" );
      } else if( a == _T( '\t' ) ) {
        dst_str << _T( '\t' );
      } else
      { dst_str << a; }
    }
  }
  return dst_str;
}



#define II_INVALID_I    0x00FFFFFF

#define ITEM_ITERATION_VARIABLES \
  wxPGProperty* parent; \
  unsigned int i; \
  unsigned int iMax;

#define ITEM_ITERATION_INIT_FROM_THE_TOP \
  parent = m_properties; \
  i = 0;

#define ITEM_ITERATION_INIT(startparent, startindex, state) \
  parent = startparent; \
  i = (unsigned int)startindex; \
  if ( parent == (wxPGProperty*) NULL ) \
  { \
    parent = state->m_properties; \
    i = 0; \
  }

#define ITEM_ITERATION_LOOP_BEGIN \
  do \
  { \
    iMax = parent->GetCount(); \
    while ( i < iMax ) \
    {  \
      wxPGProperty* p = parent->Item(i);

#define ITEM_ITERATION_LOOP_END \
  if ( p->GetChildCount() ) \
  { \
    i = 0; \
    parent = (wxPGProperty*)p; \
    iMax = parent->GetCount(); \
  } \
  else \
    i++; \
  } \
  i = parent->m_arrIndex + 1; \
  parent = parent->m_parent; \
  } \
  while ( parent != NULL );



wxPGProperty* wxPropertyGrid::DoGetItemAtY( int y ) const {
  if( y < 0 ) {
    return ( wxPGProperty* ) NULL;
  }
  unsigned int a = 0;
  return m_pState->m_properties->GetItemAtY( y, m_lineHeight, &a );
}


wxPropertyGridHitTestResult wxPropertyGrid::HitTest( const wxPoint& pt ) const {
  wxPoint pt2;
  GetViewStart( &pt2.x, &pt2.y );
  pt2.x *= wxPG_PIXELS_PER_UNIT;
  pt2.y *= wxPG_PIXELS_PER_UNIT;
  pt2.x += pt.x;
  pt2.y += pt.y;
  return m_pState->HitTest( pt2 );
}



void wxPropertyGrid::OnPaint( wxPaintEvent & ( event ) ) {
  wxPaintDC dc( this );
  wxRect r = GetUpdateRegion().GetBox();
  if( r.x > 0 && r.y > 0 ) {
    r.Inflate( 1 );
  }
  dc.SetPen( m_colEmptySpace );
  dc.SetBrush( m_colEmptySpace );
  dc.DrawRectangle( r );
}

void wxPropertyGrid::DrawExpanderButton( wxDC& dc, const wxRect& rect,
    wxPGProperty* property ) const {
  wxRect r( rect );
  r.x += m_gutterWidth;
  r.y += m_buttonSpacingY;
  r.width = m_iconWidth;
  r.height = m_iconHeight;
  #if (wxPG_USE_RENDERER_NATIVE)
  #elif wxPG_ICON_WIDTH
  dc.SetPen( m_colPropFore );
  if( property->IsCategory() ) {
    dc.SetBrush( *wxTRANSPARENT_BRUSH );
  } else {
    dc.SetBrush( m_colPropBack );
  }
  dc.DrawRectangle( r );
  int _y = r.y + ( m_iconWidth / 2 );
  dc.DrawLine( r.x + 2, _y, r.x + m_iconWidth - 2, _y );
  #else
  wxBitmap* bmp;
  #endif
  if( property->IsExpanded() ) {
    #if (wxPG_USE_RENDERER_NATIVE)
    wxRendererNative::Get().DrawTreeItemButton(
      ( wxWindow* )this,
      dc,
      r,
      wxCONTROL_EXPANDED
    );
    #elif wxPG_ICON_WIDTH
    #else
    bmp = m_collbmp;
    #endif
  } else {
    #if (wxPG_USE_RENDERER_NATIVE)
    wxRendererNative::Get().DrawTreeItemButton(
      ( wxWindow* )this,
      dc,
      r,
      0
    );
    #elif wxPG_ICON_WIDTH
    int _x = r.x + ( m_iconWidth / 2 );
    dc.DrawLine( _x, r.y + 2, _x, r.y + m_iconWidth - 2 );
    #else
    bmp = m_expandbmp;
    #endif
  }
  #if (wxPG_USE_RENDERER_NATIVE)
  #elif wxPG_ICON_WIDTH
  #else
  dc.DrawBitmap( *bmp, r.x, r.y, true );
  #endif
}



void wxPropertyGrid::DrawItems( wxDC& dc, unsigned int topy, unsigned int bottomy, const wxRect* clipRect ) {
  if( m_frozen || m_height < 1 || bottomy < topy || !m_pState ) {
    return;
  }
  m_pState->EnsureVirtualHeight();
  wxRect tempClipRect;
  if( !clipRect ) {
    tempClipRect = wxRect( 0, topy, m_pState->m_width, bottomy );
    clipRect = &tempClipRect;
  }
  if( m_pState->m_itemsAdded ) {
    PrepareAfterItemsAdded();
  }
  int paintFinishY = 0;
  if( m_pState->m_properties->GetCount() > 0 ) {
    wxDC* dcPtr = &dc;
    bool isBuffered = false;
    #if wxPG_DOUBLE_BUFFER
    wxMemoryDC* bufferDC = NULL;
    if( !( GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING ) ) {
      if( !m_doubleBuffer ) {
        paintFinishY = clipRect->y;
        dcPtr = NULL;
      } else {
        bufferDC = new wxMemoryDC();
        bufferDC->SelectObject( *m_doubleBuffer );
        dcPtr = bufferDC;
        isBuffered = true;
      }
    }
    #endif
    if( dcPtr ) {
      paintFinishY = DoDrawItems( *dcPtr, NULL, NULL, clipRect,
                                  isBuffered );
      if( paintFinishY <= ( clipRect->y + clipRect->height ) ) {
        dcPtr->SetPen( m_colEmptySpace );
        dcPtr->SetBrush( m_colEmptySpace );
        dcPtr->DrawRectangle( 0, paintFinishY, m_width,
                              dcPtr->GetSize().y - paintFinishY );
      }
    }
    #if wxPG_DOUBLE_BUFFER
    if( bufferDC ) {
      dc.Blit( clipRect->x, clipRect->y, clipRect->width,
               clipRect->height,
               bufferDC, 0, 0, wxCOPY );
      delete bufferDC;
    }
    #endif
  } else {
    dc.SetPen( m_colEmptySpace );
    dc.SetBrush( m_colEmptySpace );
    dc.DrawRectangle( *clipRect );
  }
}


int wxPropertyGrid::DoDrawItems( wxDC& dc, const wxPGProperty* firstItem, const wxPGProperty* lastItem,
                                 const wxRect* clipRect, bool isBuffered ) const {
  wxRect tempClipRect;
  if( !clipRect ) {
    wxASSERT( firstItem );
    wxASSERT( lastItem );
    tempClipRect = GetPropertyRect( firstItem, lastItem );
    clipRect = &tempClipRect;
  }
  if( !firstItem ) {
    firstItem = DoGetItemAtY( clipRect->y );
  }
  if( !lastItem ) {
    lastItem = DoGetItemAtY( clipRect->y + clipRect->height - 1 );
    if( !lastItem ) {
      lastItem = GetLastItem( wxPG_ITERATE_VISIBLE );
    }
  }
  if( m_frozen || m_height < 1 || firstItem == NULL ) {
    return 0;
  }
  wxCHECK_MSG( !m_pState->m_itemsAdded, clipRect->y, _T( "no items added" ) );
  wxASSERT( m_pState->m_properties->GetCount() );
  int lh = m_lineHeight;
  int firstItemTopY;
  int lastItemBottomY;
  firstItemTopY = clipRect->y;
  lastItemBottomY = clipRect->y + clipRect->height;
  firstItemTopY -= firstItemTopY % lh;
  lastItemBottomY += lh - ( lastItemBottomY % lh );
  lastItemBottomY -= 1;
  if( firstItemTopY >= ( int )m_pState->GetVirtualHeight() || lastItemBottomY <= 0 ) {
    return 0;
  }
  wxCHECK_MSG( firstItemTopY < lastItemBottomY, clipRect->y,
               _T( "invalid y values" ) );
  wxRect r;
  long windowStyle = m_windowStyle;
  int xRelMod = 0;
  int yRelMod = 0;
  #if wxPG_DOUBLE_BUFFER
  wxRect cr2;
  if( isBuffered ) {
    xRelMod = clipRect->x;
    yRelMod = clipRect->y;
    if( clipRect ) {
      cr2 = *clipRect;
      cr2.x -= xRelMod;
      cr2.y -= yRelMod;
      clipRect = &cr2;
    }
    firstItemTopY -= yRelMod;
    lastItemBottomY -= yRelMod;
  }
  #else
  wxUnusedVar( isBuffered );
  #endif
  int x = m_marginWidth - xRelMod;
  wxFont normalfont = GetFont();
  bool reallyFocused = ( m_iFlags & wxPG_FL_FOCUSED ) ? true : false;
  bool isEnabled = IsEnabled();
  wxPGProperty* firstSelected = GetSelection();
  wxBrush marginBrush( m_colMargin );
  wxPen marginPen( m_colMargin );
  wxBrush capbgbrush( m_colCapBack, wxSOLID );
  wxPen linepen( m_colLine, 1, wxSOLID );
  wxBrush selBackBrush;
  if( isEnabled ) {
    selBackBrush = wxBrush( m_colSelBack );
  } else {
    selBackBrush = marginBrush;
  }
  wxPen outlinepen( m_colPropFore, 1, wxSOLID );
  dc.SetBrush( marginBrush );
  if( !( windowStyle & wxPG_HIDE_MARGIN ) ) {
    dc.SetPen( *wxTRANSPARENT_PEN );
    dc.DrawRectangle( -1 - xRelMod, firstItemTopY - 1, x + 2, lastItemBottomY - firstItemTopY + 2 );
  }
  const wxPropertyGridState* state = m_pState;
  #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
  bool wasSelectedPainted = false;
  #endif
  dc.SetFont( normalfont );
  wxPropertyGridConstIterator it( state, wxPG_ITERATE_VISIBLE, firstItem );
  int endScanBottomY = lastItemBottomY + lh;
  int y = firstItemTopY;
  unsigned int arrInd = 0;
  for( ; !it.AtEnd(); it.Next() ) {
    const wxPGProperty* p = *it;
    if( !p->HasFlag( wxPG_PROP_HIDDEN ) ) {
      m_visPropArray[arrInd] = ( wxPGProperty* )p;
      arrInd++;
      if( y > endScanBottomY ) {
        break;
      }
      y += lh;
    }
  }
  m_visPropArray[arrInd] = NULL;
  int gridWidth = state->m_width;
  int rowHeight = m_fontHeight + ( m_spacingy * 2 ) + 1;
  y = firstItemTopY;
  for( arrInd = 0; m_visPropArray[arrInd] != NULL && y <= lastItemBottomY; arrInd++ ) {
    wxPGProperty* p = ( wxPGProperty* ) m_visPropArray[arrInd];
    wxPGProperty* nextP = ( wxPGProperty* ) m_visPropArray[arrInd + 1];
    wxPGProperty* parent = p->GetParent();
    int textMarginHere = x;
    int renderFlags = 0;
    int greyDepth = m_marginWidth;
    if( !( windowStyle & wxPG_HIDE_CATEGORIES ) ) {
      greyDepth = ( ( ( int )p->m_depthBgCol ) - 1 ) * m_subgroup_extramargin + m_marginWidth;
    }
    int greyDepthX = greyDepth - xRelMod;
    if( !( windowStyle & wxPG_HIDE_CATEGORIES ) || parent != m_pState->m_properties ) {
      textMarginHere += ( ( unsigned int )( ( p->m_depth - 1 ) * m_subgroup_extramargin ) );
    }
    dc.SetBrush( marginBrush );
    dc.SetPen( marginPen );
    dc.DrawRectangle( -xRelMod, y, greyDepth, lh );
    dc.SetPen( linepen );
    int y2 = y + lh;
    bool suppressMarginEdge = false;
    if( !suppressMarginEdge ) {
      dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
    } else {
      dc.SetPen( wxPen( GetBackgroundColour() ) );
      dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
      dc.SetPen( linepen );
    }
    unsigned int si;
    int sx = x;
    for( si = 0; si < state->m_colWidths.size(); si++ ) {
      sx += state->m_colWidths[si];
      dc.DrawLine( sx, y, sx, y2 );
    }
    if( p->IsCategory() &&
        nextP && nextP->IsCategory() ) {
      dc.SetPen( m_colCapBack );
    }
    dc.DrawLine( greyDepthX, y2 - 1, gridWidth - xRelMod, y2 - 1 );
    bool isSelected = state->DoIsPropertySelected( p );
    if( p == firstSelected ) {
      renderFlags |= wxPGCellRenderer::Selected;
      #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
      wasSelectedPainted = true;
      #endif
    }
    wxColour rowBgCol;
    wxColour rowFgCol;
    wxBrush rowBgBrush;
    if( p->IsCategory() ) {
      if( p->m_fgColIndex == 0 ) {
        rowFgCol = m_colCapFore;
      } else
      { rowFgCol = *( wxPGColour* )m_arrFgCols[p->m_fgColIndex]; }
      rowBgBrush = wxBrush( m_colCapBack );
    } else if( !isSelected ) {
      if( !p->IsEnabled() ) {
        rowFgCol = m_colDisPropFore;
      } else
      { rowFgCol = *( wxPGColour* )m_arrFgCols[p->m_fgColIndex]; }
      rowBgBrush = *( wxPGBrush* )m_arrBgBrushes[p->m_bgColIndex];
    } else {
      if( reallyFocused && p == firstSelected ) {
        rowFgCol = m_colSelFore;
        rowBgBrush = selBackBrush;
      } else if( isEnabled ) {
        rowFgCol = *( wxPGColour* )m_arrFgCols[p->m_fgColIndex];
        if( p == firstSelected ) {
          rowBgBrush = marginBrush;
        } else {
          rowBgBrush = selBackBrush;
        }
      } else {
        rowFgCol = m_colDisPropFore;
        rowBgBrush = selBackBrush;
      }
    }
    bool fontChanged = false;
    wxRect butRect( ( ( p->m_depth - 1 ) * m_subgroup_extramargin ) - xRelMod,
                    y,
                    m_marginWidth,
                    lh );
    if( p->IsCategory() ) {
      dc.SetFont( m_captionFont );
      fontChanged = true;
      wxRect cellRect( greyDepthX, y, gridWidth - greyDepth + 2, rowHeight - 1 );
      dc.SetBrush( rowBgBrush );
      dc.SetPen( rowBgBrush.GetColour() );
      dc.SetTextForeground( rowFgCol );
      dc.DrawRectangle( cellRect );
      wxPGCellRenderer* renderer = p->GetCellRenderer( 0 );
      renderer->Render( dc, cellRect, this, p, 0, -1, renderFlags );
      if( !HasFlag( wxPG_HIDE_MARGIN ) && p->HasVisibleChildren() ) {
        DrawExpanderButton( dc, butRect, p );
      }
    } else {
      if( butRect.x > 0 ) {
        butRect.x += IN_CELL_EXPANDER_BUTTON_X_ADJUST;
      }
      if( p->m_flags & wxPG_PROP_MODIFIED && ( windowStyle & wxPG_BOLD_MODIFIED ) ) {
        dc.SetFont( m_captionFont );
        fontChanged = true;
      }
      unsigned int ci;
      int cellX = x + 1;
      int nextCellWidth = state->m_colWidths[0] - ( greyDepthX - m_marginWidth );
      wxRect cellRect( greyDepthX + 1, y, 0, rowHeight - 1 );
      int textXAdd = textMarginHere - greyDepthX;
      for( ci = 0; ci < state->m_colWidths.size(); ci++ ) {
        cellRect.width = nextCellWidth - 1;
        wxWindow* cellEditor = NULL;
        wxWindow* editorClipperWnd = NULL;
        bool drawExpanderButton = ( ci == 0 && p->HasVisibleChildren() && !HasFlag( wxPG_HIDE_MARGIN ) );
        bool drawExpanderButtonPostClip = false;
        if( drawExpanderButton ) {
          drawExpanderButtonPostClip = true;
          if( butRect.x < cellRect.x ) {
            DrawExpanderButton( dc, butRect, p );
          } else {
            drawExpanderButtonPostClip = true;
          }
        }
        if( isSelected && ( ci == 1 || ci == m_selColumn ) ) {
          if( p == firstSelected ) {
            if( ci == 1 && m_wndEditor ) {
              editorClipperWnd = m_wndEditor;
              cellEditor = GetEditorControl();
            } else if( ci == m_selColumn && m_labelEditor ) {
              editorClipperWnd = m_labelEditor;
              cellEditor = GetLabelEditor();
            }
          }
          if( cellEditor ) {
            wxColour editorBgCol = cellEditor->GetBackgroundColour();
            dc.SetBrush( editorBgCol );
            dc.SetPen( editorBgCol );
            dc.SetTextForeground( m_colPropFore );
            if( m_dragStatus != 0 || ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE ) ) {
              cellEditor = NULL;
            }
          } else {
            dc.SetBrush( m_colPropBack );
            dc.SetPen( m_colPropBack );
            if( p->IsEnabled() ) {
              dc.SetTextForeground( m_colPropFore );
            } else {
              dc.SetTextForeground( m_colDisPropFore );
            }
          }
        } else {
          dc.SetBrush( rowBgBrush );
          dc.SetPen( rowBgBrush.GetColour() );
          dc.SetTextForeground( rowFgCol );
        }
        if( cellEditor ) {
          wxRegion clipRegion( cellRect );
          clipRegion.Subtract( editorClipperWnd->GetRect() );
          dc.SetClippingRegion( clipRegion );
        } else {
          dc.SetClippingRegion( cellRect );
        }
        dc.DrawRectangle( cellRect );
        if( drawExpanderButtonPostClip ) {
          DrawExpanderButton( dc, butRect, p );
        }
        cellRect.x += textXAdd;
        cellRect.width -= textXAdd;
        if( !cellEditor ) {
          wxPGCellRenderer* renderer;
          int cmnVal = p->GetCommonValue();
          if( cmnVal == -1 || ci != 1 ) {
            renderer = p->GetCellRenderer( ci );
            renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags );
          } else {
            renderer = GetCommonValue( cmnVal )->GetRenderer();
            renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags );
          }
        }
        cellX += state->m_colWidths[ci];
        if( ci < ( state->m_colWidths.size() - 1 ) ) {
          nextCellWidth = state->m_colWidths[ci + 1];
        }
        cellRect.x = cellX;
        dc.DestroyClippingRegion();
        textXAdd = 0;
      }
    }
    if( fontChanged ) {
      dc.SetFont( normalfont );
    }
    y += rowHeight;
  }
  #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
  if( wasSelectedPainted ) {
    if( m_wndEditor ) {
      m_wndEditor->Refresh();
    }
    if( m_wndEditor2 ) {
      m_wndEditor2->Refresh();
    }
  }
  #endif
  return y;
}


wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProperty* p2 ) const {
  wxRect r;
  if( m_width < 10 || m_height < 10 || !m_pState->m_properties->GetCount() || p1 == ( wxPGProperty* ) NULL ) {
    return wxRect( 0, 0, 0, 0 );
  }
  int vy = 0;
  int visTop = p1->GetY();
  int visBottom;
  if( p2 ) {
    visBottom = p2->GetY() + m_lineHeight;
  } else {
    visBottom = m_height + visTop;
  }
  wxPGProperty* selected = GetSelection();
  if( selected ) {
    int selectedY = selected->GetY();
    if( selectedY >= visTop && selectedY < visBottom ) {
      wxWindow* editor = GetEditorControl();
      if( editor ) {
        int visBottom2 = selectedY + editor->GetSize().y;
        if( visBottom2 > visBottom ) {
          visBottom = visBottom2;
        }
      }
    }
  }
  return wxRect( 0, visTop - vy, m_pState->m_width, visBottom - visTop );
}


void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 ) {
  if( m_frozen ) {
    return;
  }
  if( m_pState->m_itemsAdded ) {
    PrepareAfterItemsAdded();
  }
  wxRect rect = GetPropertyRect( p1, p2 );
  if( rect.width <= 0 ) {
    return;
  }
  wxRegion region( rect );
  if( m_wndEditor ) {
    region.Subtract( m_wndEditor->GetRect() );
  }
  if( m_wndEditor2 ) {
    region.Subtract( m_wndEditor2->GetRect() );
  }
  if( m_labelEditor ) {
    region.Subtract( m_labelEditor->GetRect() );
  }
  wxRegionIterator rects( region );
  while( rects ) {
    m_canvas->RefreshRect( rects.GetRect(), false );
    rects++;
  }
}

void wxPropertyGrid::RefreshProperty( wxPGProperty* p ) {
  if( m_pState->DoIsPropertySelected( p ) || p->IsChildSelected( true ) ) {
    wxArrayPGProperty selection = m_pState->m_selection;
    DoSetSelection( selection, wxPG_SEL_FORCE );
  }
  DrawItemAndChildren( p );
}

void wxPropertyGrid::RefreshEditor() {
  wxPGProperty* p = GetSelection();
  if( !p ) {
    return;
  }
  wxWindow* wnd = GetEditorControl();
  if( !wnd ) {
    return;
  }
  if( HasFlag( wxPG_BOLD_MODIFIED ) ) {
    if( p->HasFlag( wxPG_PROP_MODIFIED ) ) {
      wnd->SetFont( GetCaptionFont() );
    } else {
      wnd->SetFont( GetFont() );
    }
  }
  const wxPGEditor* editorClass = p->GetEditorClass();
  editorClass->UpdateControl( p, wnd );
  if( p->IsValueUnspecified() ) {
    editorClass->SetValueToUnspecified( p, wnd );
    SetEditorAppearance( m_unspecifiedAppearance );
  }
}


void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty* p ) {
  if( m_frozen ) {
    return;
  }
  wxPGProperty* parent = p->GetParent();
  while( parent && !parent->IsCategory() && parent->GetParent() ) {
    DrawItem( parent );
    parent = parent->GetParent();
  }
  DrawItemAndChildren( p );
}

void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p ) {
  wxCHECK_RET( p, _T( "invalid property id" ) );
  if( p->GetParentState() != m_pState ) {
    return;
  }
  if( m_pState->m_itemsAdded || m_frozen ) {
    return;
  }
  wxPGProperty* selected = GetSelection();
  if( selected && selected->GetParent() == p ) {
    RefreshEditor();
  }
  const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem();
  DrawItems( p, lastDrawn );
}


void wxPropertyGrid::Refresh( bool ( eraseBackground ),
                              const wxRect *rect ) {
  PrepareAfterItemsAdded();
  wxWindow::Refresh( false );
  if( m_canvas ) {
    m_canvas->Refresh( false, rect );
  }
  #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
  if( m_wndEditor ) {
    m_wndEditor->Refresh();
  }
  if( m_wndEditor2 ) {
    m_wndEditor2->Refresh();
  }
  #endif
}

void wxPropertyGrid::Clear() {
  m_pState->DoClear();
  m_propHover = NULL;
  m_prevVY = 0;
  RecalculateVirtualSize();
  if( !m_frozen ) {
    RefreshRect( wxRect( 0, 0, m_width, m_height ) );
  }
}

bool wxPropertyGrid::EnableCategories( bool enable ) {
  ClearSelection( false );
  if( enable ) {
    m_windowStyle &= ~( wxPG_HIDE_CATEGORIES );
  } else {
    m_windowStyle |= wxPG_HIDE_CATEGORIES;
  }
  if( !m_pState->EnableCategories( enable ) ) {
    return false;
  }
  if( !m_frozen ) {
    if( m_windowStyle & wxPG_AUTO_SORT ) {
      m_pState->m_itemsAdded = 1;
      PrepareAfterItemsAdded();
    }
  } else {
    m_pState->m_itemsAdded = 1;
  }
  Refresh();
  return true;
}

void wxPropertyGrid::SwitchState( wxPropertyGridState* pNewState ) {
  wxASSERT( pNewState );
  wxASSERT( pNewState->GetGrid() );
  if( pNewState == m_pState ) {
    return;
  }
  wxArrayPGProperty oldSelection = m_pState->m_selection;
  ClearSelection();
  m_pState->m_selection = oldSelection;
  bool orig_mode = m_pState->IsInNonCatMode();
  bool new_state_mode = pNewState->IsInNonCatMode();
  m_pState = pNewState;
  int pgWidth = GetClientSize().x;
  if( HasVirtualWidth() ) {
    int minWidth = pgWidth;
    if( pNewState->m_width < minWidth ) {
      pNewState->m_width = minWidth;
      pNewState->CheckColumnWidths();
    }
  } else {
    if( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) ) {
      pNewState->m_fSplitterX = -1.0;
    }
    pNewState->OnClientWidthChange( pgWidth, pgWidth - pNewState->m_width );
  }
  m_propHover = ( wxPGProperty* ) NULL;
  if( orig_mode != new_state_mode ) {
    EnableCategories( orig_mode ? false : true );
  } else if( !m_frozen ) {
    if( m_pState->m_itemsAdded ) {
      PrepareAfterItemsAdded();
    }
    SetSelection( m_pState->m_selection );
    RecalculateVirtualSize( 0 );
    Refresh();
  } else {
    m_pState->m_itemsAdded = 1;
  }
}

void wxPropertyGrid::Sort( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  m_pState->Sort( p );
}

void wxPropertyGrid::Sort() {
  m_pState->Sort();
  CorrectEditorWidgetPosY();
}

void wxPropertyGrid::DoSetSplitterPosition_T( int newxpos, bool refresh, int splitterIndex, bool allPages ) {
  if( ( newxpos < wxPG_DRAG_MARGIN ) ) {
    return;
  }
  wxPropertyGridState* state = m_pState;
  state->DoSetSplitterPosition( newxpos, splitterIndex, allPages );
  if( refresh ) {
    if( GetSelection() ) {
      CorrectEditorWidgetSizeX();
    }
    Refresh();
  }
}

void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing ) {
  wxPropertyGridState* state = m_pState;
  if( state ) {
    state->ResetColumnSizes( false );
  }
  if( enableAutoResizing && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) ) {
    m_iFlags &= ~( wxPG_FL_DONT_CENTER_SPLITTER );
  }
}

void wxPropertyGrid::CenterSplitter( bool enableAutoResizing ) {
  SetSplitterPosition( m_width / 2, true );
  if( enableAutoResizing && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) ) {
    m_iFlags &= ~( wxPG_FL_DONT_CENTER_SPLITTER );
  }
}

wxPGProperty* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty* p ) const {
  int vx, vy1;
  GetViewStart( &vx, &vy1 );
  vy1 *= wxPG_PIXELS_PER_UNIT;
  int vy2 = vy1 + m_height;
  int propY = p->GetY2( m_lineHeight );
  if( ( propY + m_lineHeight ) < vy1 ) {
    return DoGetItemAtY( vy1 );
  } else if( propY > vy2 ) {
    return DoGetItemAtY( vy2 );
  }
  return p;
}

void wxPropertyGrid::SetButtonShortcut( int keycode, bool ctrlDown, bool altDown ) {
  if( keycode ) {
    m_pushButKeyCode = keycode;
    m_pushButKeyCodeNeedsCtrl = ctrlDown ? 1 : 0;
    m_pushButKeyCodeNeedsAlt = altDown ? 1 : 0;
  } else {
    m_pushButKeyCode = WXK_DOWN;
    m_pushButKeyCodeNeedsCtrl = 0;
    m_pushButKeyCodeNeedsAlt = 1;
  }
}

bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) {
  if( m_inCommitChangesFromEditor ) {
    return true;
  }
  if( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT ) {
    if( m_inDoPropertyChanged ) {
      return true;
    }
    return false;
  }
  wxPGProperty* selected = GetSelection();
  if( m_wndEditor &&
      IsEditorsValueModified() &&
      ( m_iFlags & wxPG_FL_INITIALIZED ) &&
      selected ) {
    m_inCommitChangesFromEditor = 1;
    wxVariant variant( selected->GetValueRef() );
    bool valueIsPending = false;
    wxWindow* oldFocus = m_curFocused;
    bool validationFailure = false;
    bool forceSuccess = ( flags & ( wxPG_SEL_NOVALIDATE | wxPG_SEL_FORCE ) ) ? true : false;
    m_chgInfo_changedProperty = NULL;
    if( selected->GetEditorClass()->ActualGetValueFromControl( variant, selected, GetEditorControl() ) ) {
      if( DoEditorValidate() &&
          PerformValidation( selected, variant ) ) {
        valueIsPending = true;
      } else {
        validationFailure = true;
      }
    } else {
      EditorsValueWasNotModified();
    }
    bool res = true;
    m_inCommitChangesFromEditor = 0;
    if( validationFailure && !forceSuccess ) {
      if( oldFocus ) {
        oldFocus->SetFocus();
        m_curFocused = oldFocus;
      }
      res = OnValidationFailure( selected, variant );
      if( res ) {
        EditorsValueWasNotModified();
        OnValidationFailureReset( selected );
      }
    } else if( valueIsPending ) {
      DoPropertyChanged( selected, flags );
      EditorsValueWasNotModified();
    }
    return res;
  }
  return true;
}

bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue ) {
  m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior;
  m_validationInfo.m_isFailing = true;
  if( !wxPGIsVariantType( pendingValue, list ) ) {
    if( !p->ActualValidateValue( pendingValue, m_validationInfo ) ) {
      return false;
    }
  }
  wxVariant listValue = pendingValue;
  wxVariant* pPendingValue = &pendingValue;
  wxVariant* pList = NULL;
  wxPGProperty* pwc = p->GetParent();
  wxPGProperty* changedProperty = p;
  wxPGProperty* baseChangedProperty = changedProperty;
  wxVariant bcpPendingList;
  listValue = pendingValue;
  listValue.SetName( p->GetBaseName() );
  while( pwc && ( pwc->HasFlag( wxPG_PROP_AGGREGATE ) || pwc->HasFlag( wxPG_PROP_COMPOSED_VALUE ) ) ) {
    wxVariantList tempList;
    wxVariant lv( tempList, pwc->GetBaseName() );
    lv.Append( listValue );
    listValue = lv;
    pPendingValue = &listValue;
    if( pwc->HasFlag( wxPG_PROP_AGGREGATE ) ) {
      baseChangedProperty = pwc;
      bcpPendingList = lv;
    }
    changedProperty = pwc;
    pwc = pwc->GetParent();
  }
  wxVariant value;
  wxPGProperty* evtChangingProperty = changedProperty;
  if( !wxPGIsVariantType( *pPendingValue, list ) ) {
    value = *pPendingValue;
  } else {
    pList = pPendingValue;
    changedProperty->AdaptListToValue( *pPendingValue, &value );
  }
  wxVariant evtChangingValue = value;
  if( changedProperty->HasFlag( wxPG_PROP_COMPOSED_VALUE ) ) {
    evtChangingProperty = baseChangedProperty;
    if( evtChangingProperty != p ) {
      evtChangingProperty->AdaptListToValue( bcpPendingList, &evtChangingValue );
    } else {
      evtChangingValue = pendingValue;
    }
  }
  if( evtChangingProperty->HasFlag( wxPG_PROP_COMPOSED_VALUE ) ) {
    if( changedProperty == GetSelection() ) {
      wxWindow* editorCtrl = GetEditorControl();
      wxASSERT( editorCtrl->IsKindOf( CLASSINFO( wxTextCtrl ) ) );
      evtChangingValue = wxStaticCast( editorCtrl, wxTextCtrl )->GetValue();
    } else {
      wxLogDebug( _T( "WARNING: wxEVT_PG_CHANGING is about to happen with old value." ) );
    }
  }
  wxASSERT( m_chgInfo_changedProperty == NULL );
  m_chgInfo_changedProperty = changedProperty;
  m_chgInfo_baseChangedProperty = baseChangedProperty;
  m_chgInfo_pendingValue = value;
  if( pList ) {
    m_chgInfo_valueList = *pList;
  } else {
    m_chgInfo_valueList.MakeNull();
  }
  if( p != changedProperty && !wxPGIsVariantType( value, list ) ) {
    if( !changedProperty->ActualValidateValue( value, m_validationInfo ) ) {
      return false;
    }
  }
  if( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, &evtChangingValue ) ) {
    return false;
  }
  m_validationInfo.m_isFailing = false;
  return true;
}

#if wxUSE_STATUSBAR
wxStatusBar* wxPropertyGrid::GetStatusBar() {
  wxWindow* topWnd = ::wxGetTopLevelParent( this );
  if( topWnd && topWnd->IsKindOf( CLASSINFO( wxFrame ) ) ) {
    wxFrame* pFrame = wxStaticCast( topWnd, wxFrame );
    if( pFrame ) {
      return pFrame->GetStatusBar();
    }
  }
  return NULL;
}
#endif

void wxPropertyGrid::DoShowPropertyError( wxPGProperty * ( property ), const wxString& msg ) {
  if( !msg.length() ) {
    return;
  }
  #if wxUSE_STATUSBAR
  if( !wxPGGlobalVars->m_offline ) {
    wxStatusBar* pStatusBar = GetStatusBar();
    if( pStatusBar ) {
      pStatusBar->SetStatusText( msg );
      return;
    }
  }
  #endif
  ::wxMessageBox( msg, _T( "Property Error" ) );
}

void wxPropertyGrid::DoHidePropertyError( wxPGProperty * ( property ) ) {
  #if wxUSE_STATUSBAR
  if( !wxPGGlobalVars->m_offline ) {
    wxStatusBar* pStatusBar = GetStatusBar();
    if( pStatusBar ) {
      pStatusBar->SetStatusText( wxEmptyString );
      return;
    }
  }
  #endif
}

bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property,
    wxVariant& invalidValue ) {
  if( m_inOnValidationFailure ) {
    return true;
  }
  m_inOnValidationFailure = true;
  wxWindow* editor = GetEditorControl();
  int vfb = m_validationInfo.m_failureBehavior;
  if( m_inDoSelectProperty ) {
    if( property->HasFlag( wxPG_PROP_INVALID_VALUE ) ) {
      m_validationInfo.m_failureBehavior = vfb & ~( wxPG_VFB_SHOW_MESSAGE | wxPG_VFB_SHOW_MESSAGEBOX |
                                           wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR );
    }
  }
  property->OnValidationFailure( invalidValue );
  bool res = DoOnValidationFailure( property, invalidValue );
  if( !editor->IsKindOf( CLASSINFO( wxTextCtrl ) ) && property == GetSelection() ) {
    property->GetEditorClass()->UpdateControl( property, editor );
  }
  property->SetFlag( wxPG_PROP_INVALID_VALUE );
  m_validationInfo.m_failureBehavior = vfb;
  m_inOnValidationFailure = false;
  return res;
}


bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant & ( invalidValue ) ) {
  int vfb = m_validationInfo.m_failureBehavior;
  if( vfb & wxPG_VFB_BEEP ) {
    ::wxBell();
  }
  if( ( vfb & wxPG_VFB_MARK_CELL ) && !property->HasFlag( wxPG_PROP_INVALID_VALUE ) ) {
    if( !property->GetCell( 0 ) && !property->GetCell( 1 ) ) {
      wxColour vfbFg = *wxWHITE;
      wxColour vfbBg = *wxRED;
      property->SetCell( 0, new wxPGCell( property->GetLabel(), wxNullBitmap, vfbFg, vfbBg ) );
      property->SetCell( 1, new wxPGCell( property->GetDisplayedString(), wxNullBitmap, vfbFg, vfbBg ) );
      DrawItemAndChildren( property );
      if( property == GetSelection() ) {
        SetInternalFlag( wxPG_FL_CELL_OVERRIDES_SEL );
        wxWindow* editor = GetEditorControl();
        if( editor ) {
          editor->SetForegroundColour( vfbFg );
          editor->SetBackgroundColour( vfbBg );
        }
      }
    }
  }
  if( vfb & ( wxPG_VFB_SHOW_MESSAGE | wxPG_VFB_SHOW_MESSAGEBOX | wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR ) ) {
    wxString msg = m_validationInfo.m_failureMessage;
    if( !msg.length() ) {
      msg = _T( "You have entered invalid value. Press ESC to cancel editing." );
    }
    #if wxUSE_STATUSBAR
    if( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR ) {
      if( !wxPGGlobalVars->m_offline ) {
        wxStatusBar* pStatusBar = GetStatusBar();
        if( pStatusBar ) {
          pStatusBar->SetStatusText( msg );
        }
      }
    }
    #endif
    if( vfb & wxPG_VFB_SHOW_MESSAGE ) {
      DoShowPropertyError( property, msg );
    }
    if( vfb & wxPG_VFB_SHOW_MESSAGEBOX ) {
      ::wxMessageBox( msg, _T( "Property Error" ) );
    }
  }
  return ( vfb & wxPG_VFB_STAY_IN_PROPERTY ) ? false : true;
}


void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property ) {
  int vfb = m_validationInfo.m_failureBehavior;
  if( vfb & wxPG_VFB_MARK_CELL ) {
    property->SetCell( 0, NULL );
    property->SetCell( 1, NULL );
    ClearInternalFlag( wxPG_FL_CELL_OVERRIDES_SEL );
    if( property == GetSelection() && GetEditorControl() ) {
      RefreshProperty( property );
    } else {
      DrawItemAndChildren( property );
    }
  }
  #if wxUSE_STATUSBAR
  if( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR ) {
    if( !wxPGGlobalVars->m_offline ) {
      wxStatusBar* pStatusBar = GetStatusBar();
      if( pStatusBar ) {
        pStatusBar->SetStatusText( wxEmptyString );
      }
    }
  }
  #endif
  if( vfb & wxPG_VFB_SHOW_MESSAGE ) {
    DoHidePropertyError( property );
  }
  m_validationInfo.m_isFailing = false;
}


void wxPropertyGridInterface::SetValidationFailureBehavior( int vfbFlags ) {
  GetPropertyGrid()->m_permanentValidationFailureBehavior = vfbFlags;
}

bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) {
  if( m_inDoPropertyChanged ) {
    return true;
  }
  wxPGProperty* selected = GetSelection();
  m_pState->m_anyModified = 1;
  m_inDoPropertyChanged = 1;
  OnValidationFailureReset( selected );
  wxASSERT( m_chgInfo_changedProperty != NULL );
  wxPGProperty* changedProperty = m_chgInfo_changedProperty;
  wxVariant value = m_chgInfo_pendingValue;
  wxPGProperty* topPaintedProperty = changedProperty;
  while( !topPaintedProperty->IsCategory() && !topPaintedProperty->IsRoot() ) {
    topPaintedProperty = topPaintedProperty->GetParent();
  }
  changedProperty->SetValue( value, &m_chgInfo_valueList, wxPG_SETVAL_BY_USER );
  wxWindow* editor = GetEditorControl();
  if( !( p->m_flags & wxPG_PROP_MODIFIED ) ) {
    p->m_flags |= wxPG_PROP_MODIFIED;
    if( p == selected && ( m_windowStyle & wxPG_BOLD_MODIFIED ) ) {
      if( editor ) {
        SetCurControlBoldFont();
      }
    }
  }
  wxPGProperty* pwc;
  pwc = p;
  wxPGProperty* prevPwc = NULL;
  while( prevPwc != topPaintedProperty ) {
    pwc->m_flags |= wxPG_PROP_MODIFIED;
    if( pwc == selected && ( m_windowStyle & wxPG_BOLD_MODIFIED ) ) {
      if( editor ) {
        SetCurControlBoldFont();
      }
    }
    prevPwc = pwc;
    pwc = pwc->GetParent();
  }
  DrawItemAndChildren( topPaintedProperty );
  if( selFlags & wxPG_SEL_DIALOGVAL ) {
    RefreshEditor();
  } else {
    #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
    if( m_wndEditor ) {
      m_wndEditor->Refresh();
    }
    if( m_wndEditor2 ) {
      m_wndEditor2->Refresh();
    }
    #endif
  }
  wxASSERT( !changedProperty->GetParent()->HasFlag( wxPG_PROP_AGGREGATE ) );
  if( changedProperty->HasFlag( wxPG_PROP_COMPOSED_VALUE ) ) {
    pwc = m_chgInfo_baseChangedProperty;
    while( pwc != changedProperty ) {
      SendEvent( wxEVT_PG_CHANGED, pwc, NULL );
      pwc = pwc->GetParent();
    }
  }
  SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL );
  m_inDoPropertyChanged = 0;
  return true;
}


bool wxPropertyGrid::ChangePropertyValue( wxPGPropArg id, wxVariant newValue ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  m_chgInfo_changedProperty = NULL;
  if( PerformValidation( p, newValue ) ) {
    DoPropertyChanged( p );
    return true;
  } else {
    OnValidationFailure( p, newValue );
  }
  return false;
}



bool wxPropertyGrid::DoEditorValidate() {
  #if wxUSE_VALIDATORS
  if( !( GetExtraStyle() & wxPG_EX_LEGACY_VALIDATORS ) ) {
    return true;
  }
  if( m_iFlags & wxPG_FL_VALIDATION_FAILED ) {
    return false;
  }
  wxWindow* wnd = GetEditorControl();
  wxPGProperty* selected = GetSelection();
  wxValidator* validator = NULL;
  if( selected ) {
    validator = selected->GetValidator();
  }
  if( validator && wnd ) {
    if( wnd->IsKindOf( CLASSINFO( wxPGOwnerDrawnComboBox ) ) ) {
      wnd = ( ( wxPGOwnerDrawnComboBox* )wnd )->GetTextCtrl();
      if( !wnd ) {
        return true;
      }
    }
    validator->SetWindow( wnd );
    m_iFlags |= wxPG_FL_VALIDATION_FAILED;
    if( !validator->Validate( this ) ) {
      m_iFlags &= ~( wxPG_FL_VALIDATION_FAILED );
      return false;
    }
    m_iFlags &= ~( wxPG_FL_VALIDATION_FAILED );
  }
  #endif
  return true;
}

bool wxPropertyGrid::ProcessEvent( wxEvent& event ) {
  wxWindow* wnd = ( wxWindow* ) event.GetEventObject();
  if( wnd && wnd->IsKindOf( CLASSINFO( wxWindow ) ) ) {
    wxWindow* parent = wnd->GetParent();
    if( event.IsCommandEvent() && parent && ( parent == m_canvas || parent->GetParent() == m_canvas ) ) {
      if( HandleCustomEditorEvent( ( wxCommandEvent& )event ) ) {
        return true;
      }
    }
  }
  return wxPanel::ProcessEvent( event );
}

void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event ) {
  HandleCustomEditorEvent( event );
}

bool wxPropertyGrid::HandleCustomEditorEvent( wxCommandEvent &event ) {
  if( !m_pState ) {
    return false;
  }
  if( m_labelEditor && event.GetId() == m_labelEditor->GetId() ) {
    event.Skip();
    return false;
  }
  wxPGProperty* selected = GetSelection();
  if( !selected || selected->HasFlag( wxPG_PROP_BEING_DELETED ) ) {
    return false;
  }
  if( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT ||
      m_inDoSelectProperty ||
      m_inOnValidationFailure ||
      m_processedEvent ) {
    return false;
  }
  wxVariant pendingValue( selected->GetValueRef() );
  wxWindow* wnd = GetEditorControl();
  wxWindow* editorWnd = wxDynamicCast( event.GetEventObject(), wxWindow );
  int selFlags = 0;
  bool wasUnspecified = selected->IsValueUnspecified();
  int usesAutoUnspecified = selected->UsesAutoUnspecified();
  bool valueIsPending = false;
  m_chgInfo_changedProperty = NULL;
  m_iFlags &= ~( wxPG_FL_VALIDATION_FAILED | wxPG_FL_VALUE_CHANGE_IN_EVENT );
  if( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && wnd ) {
    if( wnd->IsKindOf( CLASSINFO( wxTextCtrl ) ) ) {
      wxTextCtrl* tc = ( wxTextCtrl* ) wnd;
      wxString newTcValue = tc->GetValue();
      if( m_prevTcValue == newTcValue ) {
        return false;
      }
      m_prevTcValue = newTcValue;
    } else if( wnd->IsKindOf( CLASSINFO( wxPGComboCtrl ) ) ) {
      wxPGComboCtrl* cc = ( wxPGComboCtrl* ) wnd;
      wxString newTcValue = cc->GetTextCtrl()->GetValue();
      if( m_prevTcValue == newTcValue ) {
        return false;
      }
      m_prevTcValue = newTcValue;
    }
  }
  SetInternalFlag( wxPG_FL_IN_ONCUSTOMEDITOREVENT );
  bool validationFailure = false;
  bool buttonWasHandled = false;
  if( m_wndEditor2 && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED ) {
    wxPGEditorDialogAdapter* adapter = selected->GetEditorDialog();
    if( adapter ) {
      buttonWasHandled = true;
      adapter->ShowDialog( this, selected );
      delete adapter;
    }
  }
  if( !buttonWasHandled ) {
    if( wnd || m_wndEditor2 ) {
      const wxPGEditor* editor = selected->GetEditorClass();
      if( editor->OnEvent( this, selected, editorWnd, event ) ) {
        if( DoEditorValidate() ) {
          if( editor->ActualGetValueFromControl( pendingValue, selected, wnd ) ) {
            valueIsPending = true;
          }
          if( !valueIsPending &&
              !pendingValue.IsNull() &&
              m_validationInfo.m_isFailing ) {
            valueIsPending = true;
          }
        } else {
          validationFailure = true;
        }
      }
    }
    if( !validationFailure ) {
      buttonWasHandled = selected->OnEvent( this, editorWnd, event );
    }
  }
  if( m_iFlags & wxPG_FL_VALUE_CHANGE_IN_EVENT ) {
    valueIsPending = true;
    pendingValue = m_changeInEventValue;
    selFlags |= wxPG_SEL_DIALOGVAL;
  }
  if( !validationFailure && valueIsPending )
    if( !PerformValidation( selected, pendingValue ) ) {
      validationFailure = true;
    }
  if( validationFailure ) {
    OnValidationFailure( selected, pendingValue );
  } else if( valueIsPending ) {
    selFlags |= ( !wasUnspecified && selected->IsValueUnspecified() && usesAutoUnspecified ) ? wxPG_SEL_SETUNSPEC : 0;
    DoPropertyChanged( selected, selFlags );
    EditorsValueWasNotModified();
    if( GetExtraStyle() & wxPG_EX_UNFOCUS_ON_ENTER && event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER ) {
      UnfocusEditor();
    }
  } else {
    if( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED ) {
      wxCommandEvent evt( wxEVT_COMMAND_BUTTON_CLICKED, GetId() );
      GetEventHandler()->AddPendingEvent( evt );
      buttonWasHandled = true;
    }
  }
  ClearInternalFlag( wxPG_FL_IN_ONCUSTOMEDITOREVENT );
  return buttonWasHandled;
}

wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p, int column ) const {
  int itemy = p->GetY2( m_lineHeight );
  int vy = 0;
  int splitterX = m_pState->DoGetSplitterPosition( column - 1 );
  int colEnd = splitterX + m_pState->m_colWidths[column];
  int imageOffset = 0;
  if( column == 1 ) {
    if( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE ) {
      int iw = p->OnMeasureImage().x;
      if( iw < 1 ) {
        iw = wxPG_CUSTOM_IMAGE_WIDTH;
      }
      imageOffset = p->GetImageOffset( iw );
    }
  } else if( column == 0 ) {
    splitterX += ( p->m_depth - 1 ) * m_subgroup_extramargin;
  }
  return wxRect( splitterX + imageOffset + wxPG_XBEFOREWIDGET + wxPG_CONTROL_MARGIN + 1, itemy - vy,
                 colEnd - splitterX - wxPG_XBEFOREWIDGET - wxPG_CONTROL_MARGIN - imageOffset - 1, m_lineHeight - 1 );
}


wxRect wxPropertyGrid::GetImageRect( wxPGProperty* p, int item ) const {
  wxSize sz = GetImageSize( p, item );
  return wxRect( wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1, wxPG_CUSTOM_IMAGE_SPACINGY, sz.x, sz.y );
}


wxSize wxPropertyGrid::GetImageSize( wxPGProperty* p, int item ) const {
  if( !p ) {
    return wxSize( wxPG_CUSTOM_IMAGE_WIDTH, wxPG_STD_CUST_IMAGE_HEIGHT( m_lineHeight ) );
  }
  wxSize cis = p->OnMeasureImage( item );
  int choiceCount = p->GetChoiceCount();
  int comVals = p->GetDisplayedCommonValueCount();
  if( item >= choiceCount && comVals > 0 ) {
    unsigned int cvi = item - choiceCount;
    cis = GetCommonValue( cvi )->GetRenderer()->GetImageSize( NULL, 1, cvi );
  } else if( item >= 0 && choiceCount == 0 ) {
    return wxSize( 0, 0 );
  }
  if( cis.x < 0 ) {
    if( cis.x <= -1 ) {
      cis.x = wxPG_CUSTOM_IMAGE_WIDTH;
    }
  }
  if( cis.y <= 0 ) {
    if( cis.y >= -1 ) {
      cis.y = wxPG_STD_CUST_IMAGE_HEIGHT( m_lineHeight );
    } else {
      cis.y = -cis.y;
    }
  }
  return cis;
}


void wxPropertyGrid::ImprovedClientToScreen( int* px, int* py ) {
  int vx, vy;
  GetViewStart( &vx, &vy );
  vy *= wxPG_PIXELS_PER_UNIT;
  vx *= wxPG_PIXELS_PER_UNIT;
  *px -= vx;
  *py -= vy;
  ClientToScreen( px, py );
}


void wxPropertyGrid::CustomSetCursor( int type, bool override ) {
  if( type == m_curcursor && !override ) {
    return;
  }
  wxCursor* cursor = &wxPG_DEFAULT_CURSOR;
  if( type == wxCURSOR_SIZEWE ) {
    cursor = m_cursorSizeWE;
  }
  m_canvas->SetCursor( *cursor );
  m_curcursor = type;
}


wxString wxPropertyGrid::GetUnspecifiedValueText( int argFlags ) const {
  const wxPGCell& ua = GetUnspecifiedValueAppearance();
  if( ua.HasText() &&
      !( argFlags & wxPG_FULL_VALUE ) &&
      !( argFlags & wxPG_EDITABLE_VALUE ) ) {
    return ua.GetText();
  }
  return wxEmptyString;
}

#define CONNECT_CHILD(EVT,FUNCTYPE,FUNC) \
  wnd->Connect(id, EVT, \
               (wxObjectEventFunction) (wxEventFunction)  \
               FUNCTYPE (&wxPropertyGrid::FUNC), \
               NULL, this );

void wxPropertyGrid::SetupEventHandling( wxWindow* argWnd, int id ) {
  wxWindow* wnd = argWnd;
  if( argWnd == m_wndEditor ) {
    CONNECT_CHILD( wxEVT_MOTION, ( wxMouseEventFunction ), OnMouseMoveChild )
    CONNECT_CHILD( wxEVT_LEFT_UP, ( wxMouseEventFunction ), OnMouseUpChild )
    CONNECT_CHILD( wxEVT_LEFT_DOWN, ( wxMouseEventFunction ), OnMouseClickChild )
    CONNECT_CHILD( wxEVT_RIGHT_UP, ( wxMouseEventFunction ), OnMouseRightClickChild )
    CONNECT_CHILD( wxEVT_ENTER_WINDOW, ( wxMouseEventFunction ), OnMouseEntry )
    CONNECT_CHILD( wxEVT_LEAVE_WINDOW, ( wxMouseEventFunction ), OnMouseEntry )
  } else {
    CONNECT_CHILD( wxEVT_NAVIGATION_KEY, ( wxNavigationKeyEventFunction ), OnNavigationKey )
  }
  CONNECT_CHILD( wxEVT_KEY_DOWN, ( wxCharEventFunction ), OnChildKeyDown )
  CONNECT_CHILD( wxEVT_KEY_UP, ( wxCharEventFunction ), OnChildKeyUp )
  CONNECT_CHILD( wxEVT_KILL_FOCUS, ( wxFocusEventFunction ), OnFocusEvent )
}

void wxPropertyGrid::DestroyEditorWnd( wxWindow* wnd ) {
  if( !wnd ) {
    return;
  }
  wxPendingDelete.Append( wnd );
  wnd->Hide();
}

void wxPropertyGrid::FreeEditors() {
  wxWindow* focus = wxWindow::FindFocus();
  if( focus ) {
    wxWindow* parent = focus->GetParent();
    while( parent ) {
      if( parent == m_canvas ) {
        SetFocusOnCanvas();
        break;
      }
      parent = parent->GetParent();
    }
  }
  DestroyEditorWnd( m_wndEditor2 );
  m_wndEditor2 = NULL;
  DestroyEditorWnd( m_wndEditor );
  m_wndEditor = NULL;
}

bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) {
  if( m_inDoSelectProperty ) {
    return true;
  }
  m_inDoSelectProperty = true;
  struct UnsetValue {
    UnsetValue( bool &value ) : m_value( value ) {}
    ~UnsetValue() { m_value = false; }
    bool &m_value;
  };
  UnsetValue unsetValue( m_inDoSelectProperty );
  if( !m_pState ) {
    return false;
  }
  wxArrayPGProperty prevSelection = m_pState->m_selection;
  wxPGProperty* prevFirstSel;
  if( prevSelection.size() > 0 ) {
    prevFirstSel = prevSelection[0];
  } else {
    prevFirstSel = NULL;
  }
  if( prevFirstSel && prevFirstSel->HasFlag( wxPG_PROP_BEING_DELETED ) ) {
    prevFirstSel = NULL;
  }
  DoEndLabelEdit( true, wxPG_SEL_NOVALIDATE );
  wxWindow* primaryCtrl = NULL;
  if( m_frozen ) {
    m_iFlags &= ~( wxPG_FL_ABNORMAL_EDITOR );
    m_editorFocused = 0;
    m_pState->DoSetSelection( p );
    m_selColumn = 1;
    FreeEditors();
    p = ( wxPGProperty* ) NULL;
  } else {
    if( prevFirstSel == p &&
        prevSelection.size() <= 1 &&
        !( flags & wxPG_SEL_FORCE ) ) {
      if( p ) {
        if( flags & wxPG_SEL_FOCUS ) {
          if( m_wndEditor ) {
            m_wndEditor->SetFocus();
            m_editorFocused = 1;
          }
        } else {
          SetFocusOnCanvas();
        }
      }
      return true;
    }
    if( prevFirstSel ) {
      if( p != prevFirstSel ) {
        if( !CommitChangesFromEditor( flags ) ) {
          return false;
        }
      }
      OnValidationFailureReset( prevFirstSel );
      FreeEditors();
      m_iFlags &= ~( wxPG_FL_ABNORMAL_EDITOR );
      EditorsValueWasNotModified();
    }
    SetInternalFlag( wxPG_FL_IN_SELECT_PROPERTY );
    m_pState->DoSetSelection( p );
    for( unsigned int i = 0; i < prevSelection.size(); i++ ) {
      DrawItem( prevSelection[i] );
    }
    if( p ) {
      int propY = p->GetY2( m_lineHeight );
      int splitterX = GetSplitterPosition();
      m_editorFocused = 0;
      m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
      if( p != prevFirstSel ) {
        m_iFlags &= ~( wxPG_FL_VALIDATION_FAILED );
      }
      wxASSERT( m_wndEditor == ( wxWindow* ) NULL );
      if( !p->IsCategory() && !( p->m_flags & wxPG_PROP_DISABLED ) ) {
        m_selColumn = 1;
        const wxPGEditor* editor = p->GetEditorClass();
        wxCHECK_MSG( editor, false,
                     _T( "NULL editor class not allowed" ) );
        m_iFlags &= ~( wxPG_FL_CUR_USES_CUSTOM_IMAGE );
        if( ( p->m_flags & wxPG_PROP_CUSTOMIMAGE ) &&
            !editor->CanContainCustomImage() ) {
          m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
        }
        wxRect grect = GetEditorWidgetRect( p, m_selColumn );
        wxPoint goodPos = grect.GetPosition();
        wxPGCell emptyCell;
        m_editorAppearance.Assign( emptyCell );
        m_iFlags &= ~wxPG_FL_FIXED_WIDTH_EDITOR;
        wxPGWindowList wndList = editor->CreateControls( this, p, goodPos, grect.GetSize() );
        m_wndEditor = wndList.m_primary;
        m_wndEditor2 = wndList.m_secondary;
        primaryCtrl = GetEditorControl();
        if( m_wndEditor ) {
          wxCHECK_MSG( m_wndEditor->GetParent() == m_canvas, false, _T( "CreateControls must use result of " )
                       _T( "wxPropertyGrid::GetPanel() as parent " ) _T( "of controls." ) );
          #if wxUSE_VALIDATORS
          if( !( GetExtraStyle() & wxPG_EX_LEGACY_VALIDATORS ) ) {
            wxValidator* validator = p->GetValidator();
            if( validator )
            { primaryCtrl->SetValidator( *validator ); }
          }
          #endif
          if( m_wndEditor->GetSize().y > ( m_lineHeight + 6 ) ) {
            m_iFlags |= wxPG_FL_ABNORMAL_EDITOR;
          }
          if( ( p->m_flags & wxPG_PROP_MODIFIED ) && ( m_windowStyle & wxPG_BOLD_MODIFIED ) ) {
            SetCurControlBoldFont();
          }
          wxTextCtrl* tc = NULL;
          if( primaryCtrl->IsKindOf( CLASSINFO( wxPGOwnerDrawnComboBox ) ) ) {
            tc = ( ( wxPGOwnerDrawnComboBox* )primaryCtrl )->GetTextCtrl();
          } else
          { tc = wxDynamicCast( primaryCtrl, wxTextCtrl ); }
          if( tc ) {
            wxPG_TextCtrl_SetMargins( tc, wxPoint( 0, -1 ) );
          }
          m_ctrlXAdjust = m_wndEditor->GetPosition().x - splitterX;
          wxPoint pos = m_wndEditor->GetPosition();
          if( pos.x > ( splitterX + 1 ) || pos.y > propY ) {
            m_iFlags &= ~( wxPG_FL_PRIMARY_FILLS_ENTIRE );
          }
          m_wndEditor->SetSizeHints( 3, 3 );
          if( primaryCtrl != m_wndEditor ) {
            primaryCtrl->SetSizeHints( 3, 3 );
          }
          SetupEventHandling( primaryCtrl, wxPG_SUBID1 );
          if( p->IsValueUnspecified() ) {
            editor->SetValueToUnspecified( p, primaryCtrl );
            SetEditorAppearance( m_unspecifiedAppearance );
          }
          if( flags & wxPG_SEL_FOCUS ) {
            primaryCtrl->SetFocus();
            editor->OnFocus( p, primaryCtrl );
          }
        }
        if( m_wndEditor2 ) {
          wxCHECK_MSG( m_wndEditor2->GetParent() == m_canvas, false, _T( "CreateControls must use result of " )
                       _T( "wxPropertyGrid::GetPanel() as parent " ) _T( "of controls." ) );
          m_wndSecId = m_wndEditor2->GetId();
          wxWindowList children = m_wndEditor2->GetChildren();
          wxWindowList::iterator node = children.begin();
          if( node != children.end() ) {
            m_wndSecId = ( ( wxWindow* )*node )->GetId();
          }
          m_wndEditor2->SetSizeHints( 3, 3 );
          m_wndEditor2->Show();
          SetupEventHandling( m_wndEditor2, wxPG_SUBID2 );
        }
        if( flags & wxPG_SEL_FOCUS ) {
          m_editorFocused = 1;
        }
      } else {
        SetFocusOnCanvas();
      }
      EditorsValueWasNotModified();
      if( !( flags & wxPG_SEL_NONVISIBLE ) ) {
        EnsureVisible( p );
      }
      if( m_wndEditor ) {
        m_wndEditor->Show( true );
      }
    }
    if( !( flags & wxPG_SEL_NO_REFRESH ) ) {
      DrawItem( p );
    }
    ClearInternalFlag( wxPG_FL_IN_SELECT_PROPERTY );
  }
  const wxString* pHelpString = NULL;
  if( p ) {
    pHelpString = &p->GetHelpString();
  }
  if( !( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS ) ) {
    #if wxUSE_STATUSBAR
    wxStatusBar* statusbar = GetStatusBar();
    if( statusbar ) {
      if( pHelpString && pHelpString->length() ) {
        statusbar->SetStatusText( *pHelpString );
        m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
      } else if( m_iFlags & wxPG_FL_STRING_IN_STATUSBAR ) {
        statusbar->SetStatusText( m_emptyString );
        m_iFlags &= ~( wxPG_FL_STRING_IN_STATUSBAR );
      }
    }
    #endif
  } else {
    #if wxPG_SUPPORT_TOOLTIPS
    if( pHelpString && pHelpString->length() &&
        primaryCtrl ) {
      primaryCtrl->SetToolTip( *pHelpString );
    }
    #endif
  }
  SendEvent( wxEVT_PG_SELECTED, p, NULL );
  return true;
}


bool wxPropertyGrid::UnfocusEditor() {
  wxPGProperty* selected = GetSelection();
  if( !selected || !m_wndEditor || m_frozen ) {
    return true;
  }
  if( !CommitChangesFromEditor( 0 ) ) {
    if( !( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT ) ) {
      return false;
    }
  }
  SetFocusOnCanvas();
  DrawItem( selected );
  return true;
}

bool wxPropertyGrid::DoCollapse( wxPGProperty* p, bool sendEvents ) {
  wxPGProperty* pwc = wxStaticCast( p, wxPGProperty );
  wxPGProperty* selected = GetSelection();
  if( selected && selected->IsSomeParent( p ) ) {
    if( !ClearSelection() ) {
      return false;
    }
  }
  wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
  m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
  bool res = m_pState->DoCollapse( pwc );
  if( res ) {
    if( sendEvents ) {
      SendEvent( wxEVT_PG_ITEM_COLLAPSED, p );
    }
    RecalculateVirtualSize();
    if( pwc->IsVisible() && !m_frozen && ( !pwc->IsCategory() || !( m_windowStyle & wxPG_HIDE_CATEGORIES ) ) ) {
      Refresh();
    }
  }
  m_iFlags = ( m_iFlags & ~( wxPG_FL_DONT_CENTER_SPLITTER ) ) | old_flag;
  return res;
}

bool wxPropertyGrid::DoExpand( wxPGProperty* p, bool sendEvents ) {
  wxCHECK_MSG( p, false, _T( "invalid property id" ) );
  wxPGProperty* pwc = ( wxPGProperty* )p;
  wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
  m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
  bool res = m_pState->DoExpand( pwc );
  if( res ) {
    if( sendEvents ) {
      SendEvent( wxEVT_PG_ITEM_EXPANDED, p );
    }
    RecalculateVirtualSize();
    if( pwc->IsVisible() && !m_frozen && ( !pwc->IsCategory() || !( m_windowStyle & wxPG_HIDE_CATEGORIES ) ) ) {
      #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
      Refresh();
      #else
      DrawItems( pwc, NULL );
      #endif
    }
  }
  m_iFlags = ( m_iFlags & ~( wxPG_FL_DONT_CENTER_SPLITTER ) ) | old_flag;
  return res;
}


bool wxPropertyGrid::DoHideProperty( wxPGProperty* p, bool hide, int flags ) {
  if( m_frozen ) {
    return m_pState->DoHideProperty( p, hide, flags );
  }
  wxArrayPGProperty selection = m_pState->m_selection;
  int selRemoveCount = 0;
  for( unsigned int i = 0; i < selection.size(); i++ ) {
    wxPGProperty* selected = selection[i];
    if( selected == p || selected->IsSomeParent( p ) ) {
      if( !DoRemoveFromSelection( p, flags ) ) {
        return false;
      }
      selRemoveCount += 1;
    }
  }
  m_pState->DoHideProperty( p, hide, flags );
  RecalculateVirtualSize();
  Refresh();
  return true;
}

void wxPropertyGrid::RecalculateVirtualSize( int ( forceXPos ) ) {
  if( HasInternalFlag( wxPG_FL_RECALCULATING_VIRTUAL_SIZE ) || m_frozen || !m_pState ) {
    return;
  }
  if( m_pState->m_vhCalcPending ) {
    CorrectEditorWidgetPosY();
  }
  m_pState->EnsureVirtualHeight();
  #ifdef __WXDEBUG__
  int by1 = m_pState->GetVirtualHeight();
  int by2 = m_pState->GetActualVirtualHeight();
  if( by1 != by2 ) {
    wxString s = wxString::Format( _T( "VirtualHeight=%i, ActualVirtualHeight=%i, should match!" ), by1, by2 );
    wxASSERT_MSG( false,
                  s.c_str() );
    wxLogDebug( s );
  }
  #endif
  m_iFlags |= wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
  int y = m_pState->m_virtualHeight;
  int width, height;
  GetClientSize( &width, &height );
  if( !HasVirtualWidth() ) {
    m_pState->SetVirtualWidth( width );
  }
  m_width = width;
  m_height = height;
  wxASSERT_MSG( y <= 32767, _T( "Sorry, wxPanel height limitations exceeded" ) );
  if( m_canvas ) {
    m_canvas->SetMinSize( wxSize( 10, y ) );
  }
  FitInside();
  m_pState->CheckColumnWidths();
  if( GetSelection() ) {
    CorrectEditorWidgetSizeX();
  }
  m_iFlags &= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
}

void wxPropertyGrid::OnResize( wxSizeEvent& event ) {
  if( !( m_iFlags & wxPG_FL_INITIALIZED ) ) {
    return;
  }
  int width, height;
  GetClientSize( &width, &height );
  m_width = width;
  m_height = height;
  m_visPropArray.SetCount( ( height / m_lineHeight ) + 10 );
  #if wxPG_DOUBLE_BUFFER
  if( !( GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING ) ) {
    int dblh = ( m_lineHeight * 2 );
    if( !m_doubleBuffer ) {
      int w = ( width > 250 ) ? width : 250;
      int h = height + dblh;
      h = ( h > 400 ) ? h : 400;
      m_doubleBuffer = new wxBitmap( w, h );
    } else {
      int w = m_doubleBuffer->GetWidth();
      int h = m_doubleBuffer->GetHeight();
      if( w < width || h < ( height + dblh ) ) {
        if( w < width ) {
          w = width;
        }
        if( h < ( height + dblh ) ) {
          h = height + dblh;
        }
        delete m_doubleBuffer;
        m_doubleBuffer = new wxBitmap( w, h );
      }
    }
  }
  #endif
  m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true );
  m_ncWidth = event.GetSize().x;
  if( !m_frozen ) {
    PrepareAfterItemsAdded();
    Refresh();
  }
}

void wxPropertyGrid::SetVirtualWidth( int width ) {
  if( width == -1 ) {
    width = GetClientSize().x;
    ClearInternalFlag( wxPG_FL_HAS_VIRTUAL_WIDTH );
  } else {
    SetInternalFlag( wxPG_FL_HAS_VIRTUAL_WIDTH );
  }
  m_pState->SetVirtualWidth( width );
}

void wxPropertyGridState::SetVirtualWidth( int width ) {
  if( width < 0 ) {
    width = 0;
  }
  wxPropertyGrid* pg = GetGrid();
  int gw = pg->GetClientSize().x;
  if( width < gw ) {
    width = gw;
  }
  m_width = width;
}

void wxPropertyGridState::OnClientWidthChange( int newWidth, int widthChange, bool fromOnResize ) {
  wxPropertyGrid* pg = GetGrid();
  if( pg->HasVirtualWidth() ) {
    if( m_width < newWidth ) {
      SetVirtualWidth( newWidth );
    }
    CheckColumnWidths( widthChange );
  } else {
    SetVirtualWidth( newWidth );
    if( !fromOnResize ) {
      widthChange = 0;
    }
    CheckColumnWidths( widthChange );
    if( !( GetGrid()->GetInternalFlags() & wxPG_FL_SPLITTER_PRE_SET ) &&
        ( GetGrid()->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER ) ) {
      long timeSinceCreation = ( ::wxGetLocalTimeMillis() - GetGrid()->m_timeCreated ).ToLong();
      if( timeSinceCreation < 250 ) {
        if( m_properties->GetCount() ) {
          SetSplitterLeft( false );
        } else {
          DoSetSplitterPosition( newWidth / 2 );
          GetGrid()->ClearInternalFlag( wxPG_FL_SPLITTER_PRE_SET );
        }
      }
    }
  }
}

bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, wxVariant* pValue, unsigned int selFlags,
                                unsigned int column ) {
  wxPropertyGridEvent evt( eventType, m_eventObject->GetId() );
  evt.SetPropertyGrid( this );
  evt.SetEventObject( m_eventObject );
  evt.SetProperty( p );
  evt.SetColumn( column );
  if( pValue ) {
    evt.SetCanVeto( true );
    evt.SetupValidationInfo();
    m_validationInfo.m_pValue = pValue;
  } else if( !( selFlags & wxPG_SEL_NOVALIDATE ) ) {
    evt.SetCanVeto( true );
  }
  wxEvtHandler* evtHandler = m_eventObject->GetEventHandler();
  wxPropertyGridEvent* prevProcessedEvent = m_processedEvent;
  m_processedEvent = &evt;
  evtHandler->ProcessEvent( evt );
  m_processedEvent = prevProcessedEvent;
  return evt.WasVetoed();
}

bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &event ) {
  bool res = true;
  if( !( m_iFlags & wxPG_FL_FOCUSED ) ) {
    SetFocusOnCanvas();
  }
  wxPropertyGridState* state = m_pState;
  int splitterHit;
  int splitterHitOffset;
  int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset );
  wxPGProperty* p = DoGetItemAtY( y );
  if( p ) {
    int depth = ( int )p->GetDepth() - 1;
    int marginEnds = m_marginWidth + ( depth * m_subgroup_extramargin );
    if( x >= marginEnds ) {
      if( p->IsCategory() ) {
        wxPropertyCategory* pwc = ( wxPropertyCategory* )p;
        int textX = m_marginWidth + ( ( unsigned int )( ( pwc->m_depth - 1 ) * m_subgroup_extramargin ) );
        if( x >= textX && ( x < ( textX + pwc->GetTextExtent( this, m_captionFont ) + ( wxPG_CAPRECTXMARGIN * 2 ) ) ||
                            columnHit == 0 ) ) {
          if( !AddToSelectionFromInputEvent( p, columnHit, &event ) ) {
            return res;
          }
          if( event.ButtonDClick() && !( m_windowStyle & wxPG_HIDE_MARGIN ) ) {
            if( pwc->IsExpanded() ) {
              DoCollapse( p, true );
            } else {
              DoExpand( p, true );
            }
          }
        }
      } else if( splitterHit == -1 ) {
        unsigned int selFlag = 0;
        if( columnHit == 1 ) {
          m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK;
          selFlag = wxPG_SEL_FOCUS;
        }
        if( !AddToSelectionFromInputEvent( p, columnHit, &event, selFlag ) ) {
          return res;
        }
        m_iFlags &= ~( wxPG_FL_ACTIVATION_BY_CLICK );
        if( p->GetChildCount() && !p->IsCategory() )
          if( event.ButtonDClick() && !( m_windowStyle & wxPG_HIDE_MARGIN ) ) {
            wxPGProperty* pwc = ( wxPGProperty* )p;
            if( pwc->IsExpanded() )
            { DoCollapse( p, true ); }
            else
            { DoExpand( p, true ); }
          }
        res = false;
      } else {
        if( !( m_windowStyle & wxPG_STATIC_SPLITTER ) ) {
          if( event.GetEventType() == wxEVT_LEFT_DCLICK ) {
            ResetColumnSizes( true );
          } else if( m_dragStatus == 0 ) {
            DoEndLabelEdit( true, wxPG_SEL_NOVALIDATE );
            if( m_wndEditor ) {
              if( !CommitChangesFromEditor() )
              { return res; }
              m_wndEditor->Show( false );
            }
            if( !( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) ) {
              m_canvas->CaptureMouse();
              m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
            }
            m_dragStatus = 1;
            m_draggedSplitter = splitterHit;
            m_dragOffset = splitterHitOffset;
            wxClientDC dc( m_canvas );
            #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
            if( m_wndEditor2 )
            { m_wndEditor2->Show( false ); }
            #endif
            m_startingSplitterX = x - splitterHitOffset;
          }
        }
      }
    } else {
      if( p->GetChildCount() ) {
        int nx = x + m_marginWidth - marginEnds;
        if( !p->IsCategory() ) {
          nx -= IN_CELL_EXPANDER_BUTTON_X_ADJUST;
        }
        if( ( nx >= m_gutterWidth && nx < ( m_gutterWidth + m_iconWidth ) ) ) {
          int y2 = y % m_lineHeight;
          if( ( y2 >= m_buttonSpacingY && y2 < ( m_buttonSpacingY + m_iconHeight ) ) ) {
            if( ( ( wxPGProperty* )p )->IsExpanded() )
            { DoCollapse( p, true ); }
            else
            { DoExpand( p, true ); }
          }
        }
      }
    }
  }
  return res;
}


bool wxPropertyGrid::HandleMouseRightClick( int ( x ), unsigned int ( y ),
    wxMouseEvent& event ) {
  if( m_propHover ) {
    wxPGProperty* p = m_propHover;
    AddToSelectionFromInputEvent( p, m_colHover, &event );
    SendEvent( wxEVT_PG_RIGHT_CLICK, p );
    return true;
  }
  return false;
}


bool wxPropertyGrid::HandleMouseDoubleClick( int ( x ), unsigned int ( y ),
    wxMouseEvent& event ) {
  if( m_propHover ) {
    wxPGProperty* p = m_propHover;
    AddToSelectionFromInputEvent( p, m_colHover, &event );
    SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover );
    return true;
  }
  return false;
}


#if wxPG_SUPPORT_TOOLTIPS

void wxPropertyGrid::SetToolTip( const wxString& tipString ) {
  if( tipString.length() ) {
    m_canvas->SetToolTip( tipString );
  } else {
    #if wxPG_ALLOW_EMPTY_TOOLTIPS
    m_canvas->SetToolTip( m_emptyString );
    #else
    m_canvas->SetToolTip( NULL );
    #endif
  }
}

#endif

bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event ) {
  if( m_dragStatus > 0 && !event.Dragging() ) {
    HandleMouseUp( x, y, event );
  }
  wxPropertyGridState* state = m_pState;
  int splitterHit;
  int splitterHitOffset;
  int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset );
  int splitterX = x - splitterHitOffset;
  m_colHover = columnHit;
  if( m_dragStatus > 0 ) {
    if( x > ( m_marginWidth + wxPG_DRAG_MARGIN ) &&
        x < ( m_pState->m_width - wxPG_DRAG_MARGIN ) ) {
      int newSplitterX = x - m_dragOffset;
      if( newSplitterX != splitterX ) {
        SetInternalFlag( wxPG_FL_DONT_CENTER_SPLITTER );
        state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false );
        state->m_fSplitterX = ( float ) newSplitterX;
        if( GetSelection() ) {
          CorrectEditorWidgetSizeX();
        }
        Update();
        Refresh();
      }
      m_dragStatus = 2;
    }
    return false;
  } else {
    int ih = m_lineHeight;
    int sy = y;
    #if wxPG_SUPPORT_TOOLTIPS
    wxPGProperty* prevHover = m_propHover;
    unsigned char prevSide = m_mouseSide;
    #endif
    int curPropHoverY = y - ( y % ih );
    if( ( !m_propHover ) || ( m_propHover && ( sy < m_propHoverY || sy >= ( m_propHoverY + ih ) ) ) ) {
      m_propHover = DoGetItemAtY( y );
      m_propHoverY = curPropHoverY;
      SendEvent( wxEVT_PG_HIGHLIGHTED, m_propHover );
    }
    #if wxPG_SUPPORT_TOOLTIPS
    m_mouseSide = 0;
    if( columnHit == 1 ) {
      m_mouseSide = 2;
    } else if( columnHit == 0 ) {
      m_mouseSide = 1;
    }
    if( m_windowStyle & wxPG_TOOLTIPS ) {
      wxToolTip* tooltip = m_canvas->GetToolTip();
      if( m_propHover != prevHover || prevSide != m_mouseSide ) {
        if( m_propHover && !m_propHover->IsCategory() ) {
          if( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS ) {
            wxString tipString = m_propHover->GetHelpString();
            SetToolTip( tipString );
          } else {
            wxString tipString;
            int space = 0;
            if( m_mouseSide == 1 ) {
              tipString = m_propHover->m_label;
              space = splitterX - m_marginWidth - 3;
            } else if( m_mouseSide == 2 ) {
              tipString = m_propHover->GetDisplayedString();
              space = m_width - splitterX;
              if( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE )
              { space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2; }
            }
            if( space ) {
              int tw, th;
              GetTextExtent( tipString, &tw, &th, 0, 0 );
              if( tw > space )
              { SetToolTip( tipString ); }
            } else {
              if( tooltip ) {
                #if wxPG_ALLOW_EMPTY_TOOLTIPS
                m_canvas->SetToolTip( m_emptyString );
                #else
                m_canvas->SetToolTip( NULL );
                #endif
              }
            }
          }
        } else {
          if( tooltip ) {
            #if wxPG_ALLOW_EMPTY_TOOLTIPS
            m_canvas->SetToolTip( m_emptyString );
            #else
            m_canvas->SetToolTip( NULL );
            #endif
          }
        }
      }
    }
    #endif
    if( splitterHit == -1 ||
        !m_propHover ||
        HasFlag( wxPG_STATIC_SPLITTER ) ) {
      if( m_curcursor != wxCURSOR_ARROW ) {
        CustomSetCursor( wxCURSOR_ARROW );
      }
    } else {
      if( m_propHover &&
          !m_propHover->IsCategory() &&
          !event.Dragging() ) {
        CustomSetCursor( wxCURSOR_SIZEWE, true );
        return false;
      } else {
        if( m_curcursor != wxCURSOR_ARROW ) {
          CustomSetCursor( wxCURSOR_ARROW );
        }
      }
    }
    if( ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION ) && event.LeftIsDown() && m_propHover && GetSelection() &&
        columnHit != 1 && !state->DoIsPropertySelected( m_propHover ) ) {
      const wxArrayPGProperty& selection = GetSelectedProperties();
      int iterFlags = wxPG_ITERATE_VISIBLE & ( ~wxPG_PROP_CATEGORY );
      for( int i = ( selection.size() - 1 ); i >= 0; i-- ) {
        wxPGProperty* selProp = selection[i];
        if( state->ArePropertiesAdjacent( m_propHover, selProp,
                                          iterFlags ) ) {
          DoAddToSelection( m_propHover );
          break;
        }
      }
    }
  }
  return true;
}

bool wxPropertyGrid::HandleMouseUp( int x, unsigned int ( y ), wxMouseEvent & ( event ) ) {
  wxPropertyGridState* state = m_pState;
  bool res = false;
  int splitterHit;
  int splitterHitOffset;
  state->HitTestH( x, &splitterHit, &splitterHitOffset );
  if( m_dragStatus >= 1 ) {
    m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
    if( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) {
      m_canvas->ReleaseMouse();
      m_iFlags &= ~( wxPG_FL_MOUSE_CAPTURED );
    }
    if( splitterHit == -1 ||
        !m_propHover ) {
      CustomSetCursor( wxCURSOR_ARROW );
    }
    m_dragStatus = 0;
    wxPGProperty* selected = GetSelection();
    if( !( m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE ) && selected ) {
      DrawItem( selected );
    }
    if( m_wndEditor ) {
      m_wndEditor->Show( true );
    }
    #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
    if( m_wndEditor2 ) {
      m_wndEditor2->Show( true );
    }
    #endif
    m_editorFocused = 0;
  }
  return res;
}

bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py ) {
  int splitterX = GetSplitterPosition();
  int ux = event.m_x;
  int uy = event.m_y;
  wxWindow* wnd = GetEditorControl();
  if( event.GetEventType() != wxEVT_MOTION )
    if( wnd && wnd->IsKindOf( CLASSINFO( wxPGOwnerDrawnComboBox ) ) ) {
      ( ( wxPGOwnerDrawnComboBox* )wnd )->HidePopup();
    }
  wxRect r;
  if( wnd ) {
    r = wnd->GetRect();
  }
  if( wnd == ( wxWindow* ) NULL || m_dragStatus || ( ux <= ( splitterX + wxPG_SPLITTERX_DETECTMARGIN2 ) ||
      ux >= ( r.x + r.width ) || event.m_y < r.y || event.m_y >= ( r.y + r.height ) ) ) {
    *px = ux;
    *py = uy;
    return true;
  } else {
    if( m_curcursor != wxCURSOR_ARROW ) {
      CustomSetCursor( wxCURSOR_ARROW );
    }
  }
  return false;
}

void wxPropertyGrid::OnMouseClick( wxMouseEvent &event ) {
  int x, y;
  if( OnMouseCommon( event, &x, &y ) ) {
    HandleMouseClick( x, y, event );
  }
}

void wxPropertyGrid::OnMouseRightClick( wxMouseEvent &event ) {
  int x, y;
  CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
  HandleMouseRightClick( x, y, event );
}

void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent &event ) {
  OnMouseClick( event );
  int x, y;
  CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
  HandleMouseDoubleClick( x, y, event );
}

void wxPropertyGrid::OnMouseMove( wxMouseEvent &event ) {
  int x, y;
  if( OnMouseCommon( event, &x, &y ) ) {
    HandleMouseMove( x, y, event );
  }
}

void wxPropertyGrid::OnMouseMoveBottom( wxMouseEvent & ( event ) ) {
  CustomSetCursor( wxCURSOR_ARROW );
}

void wxPropertyGrid::OnMouseUp( wxMouseEvent &event ) {
  int x, y;
  if( OnMouseCommon( event, &x, &y ) ) {
    HandleMouseUp( x, y, event );
  }
}

void wxPropertyGrid::OnMouseEntry( wxMouseEvent &event ) {
  if( event.Entering() ) {
    if( !( m_iFlags & wxPG_FL_MOUSE_INSIDE ) ) {
      wxASSERT( GetParent() );
      GetParent()->SetCursor( wxNullCursor );
      m_iFlags |= wxPG_FL_MOUSE_INSIDE;
    } else
    { GetParent()->SetCursor( wxNullCursor ); }
  } else if( event.Leaving() ) {
    m_canvas->SetCursor( wxNullCursor );
    wxPoint pt = ScreenToClient( ::wxGetMousePosition() );
    if( ( pt.x <= 0 || pt.y <= 0 || pt.x >= m_width || pt.y >= m_height ) ) {
      if( ( m_iFlags & wxPG_FL_MOUSE_INSIDE ) ) {
        m_iFlags &= ~( wxPG_FL_MOUSE_INSIDE );
      }
      if( m_dragStatus ) {
        wxPropertyGrid::HandleMouseUp( -1, 10000, event );
      }
    }
  }
  event.Skip();
}

bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent &event, int* px, int *py ) {
  wxWindow* topCtrlWnd = ( wxWindow* )event.GetEventObject();
  wxASSERT( topCtrlWnd );
  int x, y;
  event.GetPosition( &x, &y );
  AdjustPosForClipperWindow( topCtrlWnd, &x, &y );
  int splitterX = GetSplitterPosition();
  wxRect r = topCtrlWnd->GetRect();
  if( !m_dragStatus &&
      x > ( splitterX - r.x + wxPG_SPLITTERX_DETECTMARGIN2 ) &&
      y >= 0 && y < r.height \
    ) {
    if( m_curcursor != wxCURSOR_ARROW ) {
      CustomSetCursor( wxCURSOR_ARROW );
    }
    event.Skip();
  } else {
    CalcUnscrolledPosition( event.m_x + r.x, event.m_y + r.y, px, py );
    return true;
  }
  return false;
}

void wxPropertyGrid::OnMouseClickChild( wxMouseEvent &event ) {
  int x, y;
  if( OnMouseChildCommon( event, &x, &y ) ) {
    bool res = HandleMouseClick( x, y, event );
    if( !res ) {
      event.Skip();
    }
  }
}

void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent &event ) {
  int x, y;
  wxASSERT( m_wndEditor );
  wxPoint pt = m_wndEditor->GetPosition();
  CalcUnscrolledPosition( event.m_x + pt.x, event.m_y + pt.y, &x, &y );
  bool res = HandleMouseRightClick( x, y, event );
  if( !res ) {
    event.Skip();
  }
}

void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent &event ) {
  int x, y;
  if( OnMouseChildCommon( event, &x, &y ) ) {
    bool res = HandleMouseMove( x, y, event );
    if( !res ) {
      event.Skip();
    }
  }
}

void wxPropertyGrid::OnMouseUpChild( wxMouseEvent &event ) {
  int x, y;
  if( OnMouseChildCommon( event, &x, &y ) ) {
    bool res = HandleMouseUp( x, y, event );
    if( !res ) {
      event.Skip();
    }
  }
}

void wxPropertyGrid::SendNavigationKeyEvent( int dir ) {
  wxNavigationKeyEvent evt;
  evt.SetFlags( wxNavigationKeyEvent::FromTab | ( dir ? wxNavigationKeyEvent::IsForward :
                wxNavigationKeyEvent::IsBackward ) );
  evt.SetEventObject( this );
  m_canvas->GetEventHandler()->AddPendingEvent( evt );
}

int wxPropertyGrid::KeyEventToActions( wxKeyEvent &event, int* pSecond ) const {
  int keycode = event.GetKeyCode();
  int modifiers = event.GetModifiers();
  wxASSERT( !( modifiers & ~( 0xFFFF ) ) );
  int hashMapKey = ( keycode & 0xFFFF ) | ( ( modifiers & 0xFFFF ) << 16 );
  wxPGHashMapI2I::const_iterator it = m_actionTriggers.find( hashMapKey );
  if( it == m_actionTriggers.end() ) {
    return 0;
  }
  if( pSecond ) {
    int second = ( it->second >> 16 ) & 0xFFFF;
    *pSecond = second;
  }
  return ( it->second & 0xFFFF );
}

void wxPropertyGrid::AddActionTrigger( int action, int keycode, int modifiers ) {
  wxASSERT( !( modifiers & ~( 0xFFFF ) ) );
  int hashMapKey = ( keycode & 0xFFFF ) | ( ( modifiers & 0xFFFF ) << 16 );
  wxPGHashMapI2I::iterator it = m_actionTriggers.find( hashMapKey );
  if( it != m_actionTriggers.end() ) {
    wxASSERT_MSG( !( it->second & ~( 0xFFFF ) ), _T( "每个键组合最多只能添加两个单独的操作." ) );
    action = it->second | ( action << 16 );
  }
  m_actionTriggers[hashMapKey] = action;
}

void wxPropertyGrid::ClearActionTriggers( int action ) {
  wxPGHashMapI2I::iterator it;
  bool didSomething;
  do {
    didSomething = false;
    for( it = m_actionTriggers.begin(); it != m_actionTriggers.end(); it++ ) {
      if( it->second == action ) {
        m_actionTriggers.erase( it );
        didSomething = true;
        break;
      }
    }
  } while( didSomething );
}

static void CopyTextToClipboard( const wxString& text ) {
  if( wxTheClipboard->Open() ) {
    wxTheClipboard->SetData( new wxTextDataObject( text ) );
    wxTheClipboard->Close();
  }
}

void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event ) {
  wxASSERT( !m_frozen );
  if( m_frozen ) {
    return;
  }
  int keycode = event.GetKeyCode();
  if( keycode == WXK_TAB ) {
    if( HasFlag( wxTAB_TRAVERSAL ) ) {
      SendNavigationKeyEvent( event.ShiftDown() ? 0 : 1 );
    } else
    { event.Skip(); }
    return;
  }
  if( keycode == WXK_ALT ||
      keycode == WXK_CONTROL ) {
    event.Skip();
    return;
  }
  int secondAction;
  int action = KeyEventToActions( event, &secondAction );
  wxPGProperty* selected = GetSelection();
  if( selected ) {
    if( ButtonTriggerKeyTest( event ) ) {
      return;
    }
    wxPGProperty* p = selected;
    if( action == wxPG_ACTION_EDIT ) {
      DoSelectAndEdit( p, 1, wxPG_SEL_FOCUS );
    } else if( action == wxPG_ACTION_COPY ) {
      CopyTextToClipboard( p->GetDisplayedString() );
    } else {
      int selectDir = -2;
      if( p->GetChildCount() ) {
        if( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY ) {
          if( ( m_windowStyle & wxPG_HIDE_MARGIN ) || Collapse( p ) ) {
            keycode = 0;
          }
        } else if( action == wxPG_ACTION_EXPAND_PROPERTY || secondAction == wxPG_ACTION_EXPAND_PROPERTY ) {
          if( ( m_windowStyle & wxPG_HIDE_MARGIN ) || Expand( p ) ) {
            keycode = 0;
          }
        }
      }
      if( keycode ) {
        if( action == wxPG_ACTION_PREV_PROPERTY || secondAction == wxPG_ACTION_PREV_PROPERTY ) {
          selectDir = -1;
        } else if( action == wxPG_ACTION_NEXT_PROPERTY || secondAction == wxPG_ACTION_NEXT_PROPERTY ) {
          selectDir = 1;
        } else
        { event.Skip(); }
      }
      if( selectDir >= -1 ) {
        p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir );
        if( p ) {
          DoSelectProperty( p );
        }
      }
    }
  } else {
    if( action != wxPG_ACTION_CANCEL_EDIT && secondAction != wxPG_ACTION_CANCEL_EDIT ) {
      wxPGProperty* p = wxPropertyGridInterface::GetFirst();
      if( p ) {
        DoSelectProperty( p );
      }
    }
  }
}

bool wxPropertyGrid::HandleChildKey( wxKeyEvent& event ) {
  bool res = true;
  wxPGProperty* selected = GetSelection();
  if( !selected || !m_wndEditor ) {
    return true;
  }
  int action = KeyEventToAction( event );
  if( action == wxPG_ACTION_CANCEL_EDIT ) {
    if( IsEditorsValueModified() ) {
      EditorsValueWasNotModified();
      selected->GetEditorClass()->SetControlStringValue( selected, GetEditorControl(),
          selected->GetDisplayedString() );
    }
    OnValidationFailureReset( selected );
    res = false;
    UnfocusEditor();
  } else if( action == wxPG_ACTION_COPY ) {
    wxTextCtrl* tc = GetEditorTextCtrl();
    if( tc ) {
      wxString sel = tc->GetStringSelection();
      if( sel.length() ) {
        CopyTextToClipboard( sel );
      }
    } else {
      CopyTextToClipboard( selected->GetDisplayedString() );
    }
  } else if( action == wxPG_ACTION_CUT ) {
    wxTextCtrl* tc = GetEditorTextCtrl();
    if( tc ) {
      long from, to;
      tc->GetSelection( &from, &to );
      if( from < to ) {
        CopyTextToClipboard( tc->GetStringSelection() );
        if( !tc->HasFlag( wxTE_READONLY ) ) {
          tc->Remove( from, to );
        }
      }
    }
  } else if( action == wxPG_ACTION_PASTE ) {
    wxTextCtrl* tc = GetEditorTextCtrl();
    if( tc && !tc->HasFlag( wxTE_READONLY ) ) {
      if( wxTheClipboard->Open() ) {
        if( wxTheClipboard->IsSupported( wxDF_TEXT ) ) {
          wxTextDataObject data;
          wxTheClipboard->GetData( data );
          long from, to;
          tc->GetSelection( &from, &to );
          if( from < to ) {
            tc->Remove( from, to );
            tc->WriteText( data.GetText() );
          } else {
            tc->WriteText( data.GetText() );
          }
        }
        wxTheClipboard->Close();
      }
    }
  } else if( action == wxPG_ACTION_SELECT_ALL ) {
    wxTextCtrl* tc = GetEditorTextCtrl();
    if( tc ) {
      tc->SetSelection( -1, -1 );
    }
  }
  int keycode = event.GetKeyCode();
  if( keycode == WXK_PAGEUP || keycode == WXK_PAGEDOWN ) {
    res = false;
  }
  return res;
}

void wxPropertyGrid::OnKey( wxKeyEvent &event ) {
  wxWindow* focused = wxWindow::FindFocus();
  wxWindow* primaryCtrl = GetEditorControl();
  if( primaryCtrl &&
      ( focused == primaryCtrl
        || m_editorFocused ) ) {
    HandleChildKey( event );
  } else {
    HandleKeyEvent( event );
  }
}

void wxPropertyGrid::OnKeyUp( wxKeyEvent &event ) {
  m_keyComboConsumed = 0;
  event.Skip();
}

void wxPropertyGrid::OnNavigationKey( wxNavigationKeyEvent& event ) {
  if( m_iFlags & wxPG_FL_IGNORE_NEXT_NAVKEY ) {
    m_iFlags &= ~( wxPG_FL_IGNORE_NEXT_NAVKEY );
    event.Skip();
    return;
  }
  wxPGProperty* next = ( wxPGProperty* ) NULL;
  wxPGProperty* selected = GetSelection();
  int dir = event.GetDirection() ? 1 : -1;
  if( selected ) {
    if( dir == 1 && ( m_wndEditor || m_wndEditor2 ) ) {
      wxWindow* focused = wxWindow::FindFocus();
      wxWindow* wndToCheck = GetEditorControl();
      if( wndToCheck && wndToCheck->IsKindOf( CLASSINFO( wxPGOwnerDrawnComboBox ) ) ) {
        wxTextCtrl* comboTextCtrl = ( ( wxPGOwnerDrawnComboBox* )wndToCheck )->GetTextCtrl();
        if( comboTextCtrl ) {
          wndToCheck = comboTextCtrl;
        }
      }
      if( focused != wndToCheck &&
          wndToCheck ) {
        wndToCheck->SetFocus();
        if( m_wndEditor && wndToCheck == m_wndEditor ) {
          selected->GetEditorClass()->OnFocus( selected, wndToCheck );
        }
        m_editorFocused = 1;
        next = selected;
      }
    }
    if( !next ) {
      next = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, selected, dir );
      if( next ) {
        DoSelectProperty( next, wxPG_SEL_FOCUS );
      }
    }
  }
  if( !next ) {
    event.Skip();
  }
}

bool wxPropertyGrid::ButtonTriggerKeyTest( wxKeyEvent &event ) {
  int keycode = event.GetKeyCode();
  if( keycode == m_pushButKeyCode &&
      m_wndEditor2 &&
      ( !m_pushButKeyCodeNeedsAlt || event.AltDown() ) &&
      ( !m_pushButKeyCodeNeedsCtrl || event.ControlDown() ) ) {
    m_keyComboConsumed = 1;
    wxCommandEvent evt( wxEVT_COMMAND_BUTTON_CLICKED, m_wndEditor2->GetId() );
    GetEventHandler()->AddPendingEvent( evt );
    return true;
  }
  return false;
}

void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event ) {
  int keycode = event.GetKeyCode();
  if( keycode == WXK_ALT ||
      keycode == WXK_CONTROL ) {
    event.Skip();
    return;
  }
  if( ButtonTriggerKeyTest( event ) ) {
    return;
  }
  if( HandleChildKey( event ) == true ) {
    event.Skip();
  }
  GetEventHandler()->AddPendingEvent( event );
}

void wxPropertyGrid::OnChildKeyUp( wxKeyEvent &event ) {
  m_keyComboConsumed = 0;
  GetEventHandler()->AddPendingEvent( event );
  event.Skip();
}

void wxPropertyGrid::OnIdle( wxIdleEvent & ( event ) ) {
  wxWindow* newFocused = wxWindow::FindFocus();
  if( newFocused != m_curFocused ) {
    HandleFocusChange( newFocused );
  }
  if( !( GetExtraStyle() & wxPG_EX_DISABLE_TLP_TRACKING ) ) {
    wxWindow* tlp = ::wxGetTopLevelParent( this );
    if( tlp != m_tlp ) {
      OnTLPChanging( tlp );
    }
  }
  if( m_deletedProperties.size() > 0 ) {
    wxArrayPGProperty& arr = m_deletedProperties;
    for( unsigned int i = 0; i < arr.size(); i++ ) {
      DeleteProperty( arr[i] );
    }
    arr.clear();
  }
  if( m_removedProperties.size() > 0 ) {
    wxArrayPGProperty& arr = m_removedProperties;
    for( unsigned int i = 0; i < arr.size(); i++ ) {
      RemoveProperty( arr[i] );
    }
    arr.clear();
  }
}

bool wxPropertyGrid::IsEditorFocused() const {
  wxWindow* focus = wxWindow::FindFocus();
  if( focus == m_wndEditor || focus == m_wndEditor2 ||
      focus == GetEditorControl() ) {
    return true;
  }
  return false;
}

void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused ) {
  unsigned int oldFlags = m_iFlags;
  bool wasEditorFocused = false;
  wxWindow* wndEditor = m_wndEditor;
  m_iFlags &= ~( wxPG_FL_FOCUSED );
  wxWindow* parent = newFocused;
  while( parent ) {
    if( parent == wndEditor ) {
      wasEditorFocused = true;
    } else if( parent == m_eventObject ) {
      m_iFlags |= wxPG_FL_FOCUSED;
      break;
    }
    parent = parent->GetParent();
  }
  if( wasEditorFocused && m_curFocused != newFocused ) {
    wxPGProperty* p = GetSelection();
    if( p ) {
      const wxPGEditor* editor = p->GetEditorClass();
      ResetEditorAppearance();
      editor->OnFocus( p, GetEditorControl() );
    }
  }
  m_curFocused = newFocused;
  if( ( m_iFlags & wxPG_FL_FOCUSED ) !=
      ( oldFlags & wxPG_FL_FOCUSED ) ) {
    if( !( m_iFlags & wxPG_FL_FOCUSED ) ) {
      m_iFlags |= wxPG_FL_IGNORE_NEXT_NAVKEY;
      CommitChangesFromEditor();
    } else
    { m_iFlags &= ~( wxPG_FL_IGNORE_NEXT_NAVKEY ); }
    wxPGProperty* selected = GetSelection();
    if( selected && ( m_iFlags & wxPG_FL_INITIALIZED ) ) {
      DrawItem( selected );
    }
  }
}

void wxPropertyGrid::OnFocusEvent( wxFocusEvent& event ) {
  if( event.GetEventType() == wxEVT_SET_FOCUS ) {
    HandleFocusChange( ( wxWindow* )event.GetEventObject() );
  } else {
    HandleFocusChange( event.GetWindow() );
  }
  event.Skip();
}


void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent& event ) {
  HandleFocusChange( ( wxWindow* )event.GetEventObject() );
}


void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent &event ) {
  m_iFlags |= wxPG_FL_SCROLLED;
  event.Skip();
}


void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent & ( event ) ) {
  if( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) {
    m_iFlags &= ~( wxPG_FL_MOUSE_CAPTURED );
  }
}

void wxPGProperty::SetAttributes( const wxPGAttributeStorage& attributes ) {
  wxPGAttributeStorage::const_iterator it = attributes.StartIteration();
  wxVariant variant;
  while( attributes.GetNext( it, variant ) ) {
    SetAttribute( variant.GetName(), variant );
  }
}

wxString wxPropertyGridInterface::GetPropertyShortClassName( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxEmptyString )
  if( !p->IsCategory() ) {
    const wxChar* src = p->GetClassName();
    wxString s;
    if( src[0] == _T( 'w' ) && src[1] == _T( 'x' ) ) {
      s = &src[2];
    } else
    { s = src; }
    wxASSERT( ( ( ( int )s.length() ) - 8 ) > 0 );
    s.Truncate( s.length() - 8 );
    return s;
  }
  return _T( "Category" );
}

wxPGProperty* wxPropertyGridInterface::GetPropertyByNameA( wxPGPropNameStr name ) const {
  wxPGProperty* p = GetPropertyByName( name );
  wxASSERT_MSG( p, wxString::Format( _T( "没有名为的属性 '%s'" ), name.c_str() ) );
  return p;
}

void wxPropertyGridInterface::PropertiesToNames( wxArrayString* names,
    const wxArrayPGProperty& properties ) const {
  unsigned int i;
  for( i = 0; i < properties.size(); i++ ) {
    names->Add( properties[i]->GetName() );
  }
}

void wxPropertyGridInterface::NamesToProperties( wxArrayPGProperty* properties,
    const wxArrayString& names ) const {
  unsigned int i;
  for( i = 0; i < names.size(); i++ ) {
    wxPGProperty* p = GetPropertyByName( names[i] );
    if( p ) {
      properties->push_back( p );
    }
  }
}

IMPLEMENT_ABSTRACT_CLASS( wxPGVariantData, wxVariantData )
WX_PG_IMPLEMENT_VARIANT_DATA( wxPGVariantDataPoint, wxPoint )
WX_PG_IMPLEMENT_VARIANT_DATA( wxPGVariantDataSize, wxSize )
WX_PG_IMPLEMENT_VARIANT_DATA( wxPGVariantDataArrayInt, wxArrayInt )
WX_PG_IMPLEMENT_VARIANT_DATA( wxPGVariantDataLongLong, wxLongLong )
WX_PG_IMPLEMENT_VARIANT_DATA( wxPGVariantDataULongLong, wxULongLong )
WX_PG_IMPLEMENT_WXOBJECT_VARIANT_DATA_WITH_PROPER_EQ( wxPGVariantDataFont, wxFont )

wxObject* wxPG_VariantToWxObject( const wxVariant& variant, wxClassInfo* classInfo ) {
  if( !variant.IsValueKindOf( classInfo ) ) {
    return ( wxObject* ) NULL;
  }
  wxVariantData* vdata = variant.GetData();
  wxPGVariantData* pgvdata = wxDynamicCastVariantData( vdata, wxPGVariantData );
  if( pgvdata ) {
    return ( wxObject* ) pgvdata->GetValuePtr();
  }
  if( wxPGIsVariantClassInfo( wxPGVariantDataGetClassInfo( vdata ), wxobject ) ) {
    return variant.GetWxObjectPtr();
  }
  return ( wxObject* ) NULL;
}

long wxPGVariantToInt( const wxVariant& variant, long defVal ) {
  if( variant.IsNull() ) {
    return defVal;
  }
  if( wxPGIsVariantType( variant, long ) ) {
    return variant.GetLong();
  }
  if( wxPGIsVariantType( variant, bool ) ) {
    return variant.GetBool() ? 1 : 0;
  }
  if( wxPGVariantDataGetClassInfo( variant.GetData() ) == &wxPGVariantDataLongLong::ms_classInfo ) {
    wxLongLong ll = ( ( const wxPGVariantDataLongLong& )variant ).GetValue();
    if( ll >= LONG_MAX ) {
      return LONG_MAX;
    } else if( ll <= LONG_MIN ) {
      return LONG_MIN;
    }
    return ll.ToLong();
  }
  long l = defVal;
  if( wxPGIsVariantType( variant, string ) ) {
    variant.GetString().ToLong( &l, 0 );
  }
  return l;
}

bool wxPGVariantToLongLong( const wxVariant& variant, wxLongLong_t* pResult ) {
  if( variant.IsNull() ) {
    return false;
  }
  if( wxPGIsVariantType( variant, long ) ) {
    *pResult = variant.GetLong();
    return true;
  }
  if( wxPGVariantDataGetClassInfo( variant.GetData() ) == &wxPGVariantDataLongLong::ms_classInfo ) {
    *pResult = ( ( const wxPGVariantDataLongLong& )variant ).GetValue().GetValue();
    return true;
  }
  return false;
}

bool wxPGVariantToULongLong( const wxVariant& variant, wxULongLong_t* pResult ) {
  if( variant.IsNull() ) {
    return false;
  }
  if( wxPGIsVariantType( variant, long ) ) {
    *pResult = ( unsigned long )variant.GetLong();
    return true;
  }
  if( wxPGVariantDataGetClassInfo( variant.GetData() ) == &wxPGVariantDataULongLong::ms_classInfo ) {
    *pResult = ( ( const wxPGVariantDataULongLong& )variant ).GetValue().GetValue();
    return true;
  }
  return false;
}

bool wxPGVariantToDouble( const wxVariant& variant, double* pResult ) {
  if( variant.IsNull() ) {
    return false;
  }
  if( wxPGIsVariantType( variant, double ) ) {
    *pResult = variant.GetDouble();
    return true;
  }
  if( wxPGIsVariantType( variant, long ) ) {
    *pResult = ( double )variant.GetLong();
    return true;
  }
  if( wxPGVariantDataGetClassInfo( variant.GetData() ) == &wxPGVariantDataLongLong::ms_classInfo ) {
    wxLongLong ll = ( ( const wxPGVariantDataLongLong& )variant ).GetValue();
    *pResult = ll.ToDouble();
    return true;
  }
  if( wxPGIsVariantType( variant, string ) )
    if( variant.GetString().ToDouble( pResult ) ) {
      return true;
    }
  return false;
}

bool wxPGVariantToWxObjectPtr( const wxVariant& value, wxObject** result ) {
  wxVariantData* vdata = value.GetData();
  if( !vdata->GetValueClassInfo() ) {
    return false;
  }
  wxPGVariantData* pgvdata = wxDynamicCastVariantData( vdata, wxPGVariantData );
  if( pgvdata ) {
    *result = ( wxObject* ) pgvdata->GetValuePtr();
    return true;
  }
  if( wxPGIsVariantClassInfo( wxPGVariantDataGetClassInfo( vdata ), wxobject ) ) {
    *result = value.GetWxObjectPtr();
    return true;
  }
  return false;
}

wxPGEditor* wxPropertyGrid::RegisterEditorClass( wxPGEditor* editorclass,
    const wxString& name,
    bool noDefCheck ) {
  wxASSERT( editorclass );
  if( !noDefCheck && wxPGGlobalVars->m_mapEditorClasses.empty() ) {
    RegisterDefaultEditors();
  }
  wxPGGlobalVars->m_mapEditorClasses[name] = ( void* )editorclass;
  return editorclass;
}

void wxPropertyGrid::RegisterDefaultEditors() {
  wxPGRegisterDefaultEditorClass( TextCtrl );
  wxPGRegisterDefaultEditorClass( Choice );
  wxPGRegisterDefaultEditorClass( ComboBox );
  wxPGRegisterDefaultEditorClass( TextCtrlAndButton );
  #if wxPG_INCLUDE_CHECKBOX
  wxPGRegisterDefaultEditorClass( CheckBox );
  #endif
  wxPGRegisterDefaultEditorClass( ChoiceAndButton );
  RegisterAdditionalEditors();
}

wxPGEditor* wxPropertyGridInterface::GetEditorByName( const wxString& editor_name ) {
  wxPGEditor* editor = ( wxPGEditor* ) wxPGGlobalVars->m_mapEditorClasses[editor_name];
  wxASSERT_MSG( editor,
                _T( "unregistered editor name" ) );
  return editor;
}

wxPGAttributeStorage::wxPGAttributeStorage() {
}

wxPGAttributeStorage::~wxPGAttributeStorage() {
  wxPGHashMapS2P::iterator it;
  for( it = m_map.begin(); it != m_map.end(); it++ ) {
    wxVariantData* data = ( wxVariantData* ) it->second;
    data->DecRef();
  }
}

void wxPGAttributeStorage::Set( const wxString& name, const wxVariant& value ) {
  wxVariantData* data = value.GetData();
  wxPGHashMapS2P::iterator it = m_map.find( name );
  if( it != m_map.end() ) {
    ( ( wxVariantData* )it->second )->DecRef();
    if( !data ) {
      m_map.erase( it );
      return;
    }
  }
  if( data ) {
    data->IncRef();
    m_map[name] = data;
  }
}

wxPGStringTokenizer::wxPGStringTokenizer( const wxString& str, wxChar delimeter )
  : m_str( &str ), m_curPos( str.begin() ), m_delimeter( delimeter ) {
}

wxPGStringTokenizer::~wxPGStringTokenizer() {
}

bool wxPGStringTokenizer::HasMoreTokens() {
  const wxString& str = *m_str;
  wxString::const_iterator i = m_curPos;
  wxUniChar delim = m_delimeter;
  wxUniChar a;
  wxUniChar prev_a = _T( '\0' );
  bool inToken = false;
  while( i != str.end() ) {
    a = wxPGGetIterChar( str, i );
    if( !inToken ) {
      if( a == delim ) {
        inToken = true;
        m_readyToken.clear();
      }
    } else {
      if( prev_a != _T( '\\' ) ) {
        if( a != delim ) {
          if( a != _T( '\\' ) ) {
            m_readyToken << a;
          }
        } else {
          i++;
          m_curPos = i;
          return true;
        }
        prev_a = a;
      } else {
        m_readyToken << a;
        prev_a = _T( '\0' );
      }
    }
    i++;
  }
  m_curPos = str.end();
  if( inToken ) {
    return true;
  }
  return false;
}

wxString wxPGStringTokenizer::GetNextToken() {
  return m_readyToken;
}

wxPGChoiceEntry::wxPGChoiceEntry()
  : wxPGCell(), m_value( wxPG_INVALID_VALUE ) {
}

wxPGChoiceEntry::wxPGChoiceEntry( const wxPGChoiceEntry& entry )
  : wxPGCell( entry.GetText(), entry.GetBitmap(),
              entry.GetFgCol(), entry.GetBgCol() ), m_value( entry.GetValue() ) {
}

wxPGChoicesData::wxPGChoicesData() {
  m_refCount = 1;
}

wxPGChoicesData::~wxPGChoicesData() {
  Clear();
}

void wxPGChoicesData::Clear() {
  unsigned int i;
  for( i = 0; i < m_items.size(); i++ ) {
    delete Item( i );
  }
  #if wxPG_USE_STL
  m_items.resize( 0 );
  #else
  m_items.Empty();
  #endif
}

void wxPGChoicesData::CopyDataFrom( wxPGChoicesData* data ) {
  wxASSERT( m_items.size() == 0 );
  unsigned int i;
  for( i = 0; i < data->GetCount(); i++ ) {
    m_items.push_back( new wxPGChoiceEntry( *data->Item( i ) ) );
  }
}

wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, int value ) {
  EnsureData();
  wxPGChoiceEntry* p = new wxPGChoiceEntry( label, value );
  m_data->Insert( -1, p );
  return *p;
}

wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, const wxBitmap& bitmap, int value ) {
  EnsureData();
  wxPGChoiceEntry* p = new wxPGChoiceEntry( label, value );
  p->SetBitmap( bitmap );
  m_data->Insert( -1, p );
  return *p;
}

wxPGChoiceEntry& wxPGChoices::Insert( const wxPGChoiceEntry& entry, int index ) {
  EnsureData();
  wxPGChoiceEntry* p = new wxPGChoiceEntry( entry );
  m_data->Insert( index, p );
  return *p;
}

wxPGChoiceEntry& wxPGChoices::Insert( const wxString& label, int index, int value ) {
  EnsureData();
  wxPGChoiceEntry* p = new wxPGChoiceEntry( label, value );
  m_data->Insert( index, p );
  return *p;
}


wxPGChoiceEntry& wxPGChoices::AddAsSorted( const wxString& label, int value ) {
  EnsureData();
  size_t index = 0;
  while( index < GetCount() ) {
    int cmpRes = GetLabel( index ).Cmp( label );
    if( cmpRes > 0 ) {
      break;
    }
    index++;
  }
  wxPGChoiceEntry* p = new wxPGChoiceEntry( label, value );
  m_data->Insert( index, p );
  return *p;
}


void wxPGChoices::Add( const wxChar** labels, const ValArrItem* values ) {
  EnsureData();
  unsigned int itemcount = 0;
  const wxChar** p = &labels[0];
  while( *p ) {
    p++;
    itemcount++;
  }
  unsigned int i;
  for( i = 0; i < itemcount; i++ ) {
    int value = wxPG_INVALID_VALUE;
    if( values ) {
      value = values[i];
    }
    m_data->Insert( -1, new wxPGChoiceEntry( labels[i], value ) );
  }
}


void wxPGChoices::Add( const wxArrayString& arr, const ValArrItem* values ) {
  EnsureData();
  unsigned int i;
  unsigned int itemcount = arr.GetCount();
  for( i = 0; i < itemcount; i++ ) {
    int value = wxPG_INVALID_VALUE;
    if( values ) {
      value = values[i];
    }
    m_data->Insert( -1, new wxPGChoiceEntry( arr[i], value ) );
  }
}

void wxPGChoices::Add( const wxArrayString& arr, const wxArrayInt& arrint ) {
  EnsureData();
  unsigned int i;
  unsigned int itemcount = arr.GetCount();
  for( i = 0; i < itemcount; i++ ) {
    int value = wxPG_INVALID_VALUE;
    if( arrint.size() ) {
      value = arrint[i];
    }
    m_data->Insert( -1, new wxPGChoiceEntry( arr[i], value ) );
  }
}

void wxPGChoices::RemoveAt( size_t nIndex, size_t count ) {
  wxASSERT( m_data->m_refCount != 0xFFFFFFF );
  unsigned int i;
  for( i = nIndex; i < ( nIndex + count ); i++ ) {
    delete m_data->Item( i );
  }
  m_data->m_items.RemoveAt( nIndex, count );
}

int wxPGChoices::Index( const wxString& str ) const {
  if( IsOk() ) {
    unsigned int i;
    for( i = 0; i < m_data->GetCount(); i++ ) {
      if( m_data->Item( i )->GetText() == str ) {
        return i;
      }
    }
  }
  return -1;
}

int wxPGChoices::Index( int val ) const {
  if( IsOk() ) {
    unsigned int i;
    for( i = 0; i < m_data->GetCount(); i++ ) {
      if( m_data->Item( i )->GetValue() == val ) {
        return i;
      }
    }
  }
  return -1;
}

wxArrayString wxPGChoices::GetLabels() const {
  wxArrayString arr;
  unsigned int i;
  if( IsOk() )
    for( i = 0; i < GetCount(); i++ ) {
      arr.push_back( GetLabel( i ) );
    }
  return arr;
}

wxArrayInt wxPGChoices::GetValuesForStrings( const wxArrayString& strings ) const {
  wxArrayInt arr;
  if( IsOk() ) {
    unsigned int i;
    for( i = 0; i < strings.size(); i++ ) {
      int index = Index( strings[i] );
      if( index >= 0 ) {
        arr.Add( GetValue( index ) );
      } else {
        arr.Add( wxPG_INVALID_VALUE );
      }
    }
  }
  return arr;
}

wxArrayInt wxPGChoices::GetIndicesForStrings( const wxArrayString& strings,
    wxArrayString* unmatched ) const {
  wxArrayInt arr;
  if( IsOk() ) {
    unsigned int i;
    for( i = 0; i < strings.size(); i++ ) {
      const wxString& str = strings[i];
      int index = Index( str );
      if( index >= 0 ) {
        arr.Add( index );
      } else if( unmatched ) {
        unmatched->Add( str );
      }
    }
  }
  return arr;
}

void wxPGChoices::AssignData( wxPGChoicesData* data ) {
  Free();
  if( data != wxPGChoicesEmptyData ) {
    m_data = data;
    data->m_refCount++;
  }
}

void wxPGChoices::Init() {
  m_data = wxPGChoicesEmptyData;
}

void wxPGChoices::Free() {
  if( m_data != wxPGChoicesEmptyData ) {
    m_data->DecRef();
    m_data = wxPGChoicesEmptyData;
  }
}

wxClassInfo wxPropertyGridEvent::ms_classInfo( wxT( "wxPropertyGridEvent" ), &wxCommandEvent::ms_classInfo, NULL,
    ( int ) sizeof( wxPropertyGridEvent ), ( wxObjectConstructorFn ) wxPropertyGridEvent::wxCreateObject );

wxClassInfo *wxPropertyGridEvent::GetClassInfo() const {
  return &wxPropertyGridEvent::ms_classInfo;
}

wxObject* wxPropertyGridEvent::wxCreateObject() {
  return new wxPropertyGridEvent;
}

void wxPropertyGridEvent::Init() {
  m_validationInfo = NULL;
  m_column = 1;
  m_canVeto = false;
  m_wasVetoed = false;
}

wxPropertyGridEvent::wxPropertyGridEvent( wxEventType commandType, int id )
  : wxCommandEvent( commandType, id ) {
  m_property = NULL;
  Init();
}

wxPropertyGridEvent::wxPropertyGridEvent( const wxPropertyGridEvent& event )
  : wxCommandEvent( event ) {
  m_eventType = event.GetEventType();
  m_eventObject = event.m_eventObject;
  m_pg = event.m_pg;
  m_property = event.m_property;
  m_validationInfo = event.m_validationInfo;
  m_canVeto = event.m_canVeto;
  m_wasVetoed = event.m_wasVetoed;
}

wxPropertyGridEvent::~wxPropertyGridEvent() {
}

wxEvent* wxPropertyGridEvent::Clone() const {
  return new wxPropertyGridEvent( *this );
}

void wxPropertyGridInterface::DoSetPropertyAttribute( wxPGPropArg id, const wxString& name,
    wxVariant& value, long argFlags ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  p->SetAttribute( name, value );
  if( argFlags & wxPG_RECURSE ) {
    unsigned int i;
    for( i = 0; i < p->GetChildCount(); i++ ) {
      DoSetPropertyAttribute( p->Item( i ), name, value, argFlags );
    }
  }
}

void wxPropertyGrid::SetPropertyAttributeAll( const wxString& attrName, wxVariant value ) {
  DoSetPropertyAttribute( GetRoot(), attrName, value, wxPG_RECURSE );
}

void wxPropertyGridInterface::GetPropertiesWithFlag( wxArrayPGProperty* targetArr,
    wxPGProperty::FlagType flags,
    bool inverse,
    int iterFlags ) const {
  wxASSERT( targetArr );
  wxPGVIterator it = GetVIterator( iterFlags );
  for( ;
       !it.AtEnd();
       it.Next() ) {
    const wxPGProperty* property = it.GetProperty();
    if( !inverse ) {
      if( ( property->GetFlags() & flags ) == flags ) {
        targetArr->push_back( ( wxPGProperty* )property );
      }
    } else {
      if( ( property->GetFlags() & flags ) != flags ) {
        targetArr->push_back( ( wxPGProperty* )property );
      }
    }
  }
}

void wxPropertyGridInterface::SetPropertiesFlag( const wxArrayPGProperty& srcArr,
    wxPGProperty::FlagType flags,
    bool inverse ) {
  unsigned int i;
  for( i = 0; i < srcArr.size(); i++ ) {
    wxPGProperty* property = srcArr[i];
    if( !inverse ) {
      property->SetFlag( flags );
    } else
    { property->ClearFlag( flags ); }
  }
  wxPropertyGrid* pg = GetPropertyGrid();
  if( flags & ( wxPG_PROP_COLLAPSED | wxPG_PROP_HIDDEN ) ) {
    GetState()->VirtualHeightChanged();
    pg->RecalculateVirtualSize();
  }
}

void wxPropertyGridInterface::SetBoolChoices( const wxString& trueChoice,
    const wxString& falseChoice ) {
  wxPGGlobalVars->m_boolChoices[0] = falseChoice;
  wxPGGlobalVars->m_boolChoices[1] = trueChoice;
}


wxPGChoices gs_emptyChoices;

wxPGChoices& wxPropertyGridInterface::GetPropertyChoices( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( gs_emptyChoices )
  wxPGChoiceInfo ci;
  ci.m_choices = ( wxPGChoices* ) NULL;
  p->GetChoiceInfo( &ci );
  if( !ci.m_choices ) {
    return gs_emptyChoices;
  }
  return *ci.m_choices;
}

wxPGProperty* wxPropertyGridInterface::DoGetPropertyByName( wxPGPropNameStr name ) const {
  return m_pState->BaseGetPropertyByName( name );
}

wxPGProperty* wxPropertyGridInterface::GetPropertyByName( wxPGPropNameStr name,
    wxPGPropNameStr subname ) const {
  wxPGProperty* p = DoGetPropertyByName( name );
  if( !p || !p->GetChildCount() ) {
    return wxNullProperty;
  }
  return p->GetPropertyByName( subname );
}

wxPGProperty* wxPropertyGridInterface::GetPropertyByName( wxPGPropNameStr name ) const {
  wxPGProperty* p = DoGetPropertyByName( name );
  if( p ) {
    return p;
  }
  int pos = name.Find( _T( '.' ) );
  if( pos <= 0 ) {
    return NULL;
  }
  return GetPropertyByName( name.substr( 0, pos ), name.substr( pos + 1, name.length() - pos - 1 ) );
}

bool wxPropertyGridInterface::HideProperty( wxPGPropArg id, bool hide, int flags ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  wxPropertyGrid* pg = m_pState->GetGrid();
  if( pg == p->GetGrid() ) {
    return pg->DoHideProperty( p, hide, flags );
  } else {
    m_pState->DoHideProperty( p, hide, flags );
  }
  return true;
}

bool wxPropertyGridInterface::Collapse( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  wxPropertyGrid* pg = p->GetGridIfDisplayed();
  if( pg ) {
    return pg->DoCollapse( p, true );
  }
  return p->GetParentState()->DoCollapse( p );
}

bool wxPropertyGridInterface::Expand( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  wxPropertyGrid* pg = p->GetGridIfDisplayed();
  if( pg ) {
    return pg->DoExpand( p, true );
  }
  return p->GetParentState()->DoExpand( p );
}

void wxPropertyGridInterface::SetPropertyLabel( wxPGPropArg id, const wxString& newproplabel ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  p->SetLabel( newproplabel );
  wxPropertyGridState* state = p->GetParentState();
  wxPropertyGrid* pg = state->GetGrid();
  if( pg->HasFlag( wxPG_AUTO_SORT ) ) {
    pg->Sort( p->GetParent() );
  }
  if( pg->GetState() == state ) {
    if( pg->HasFlag( wxPG_AUTO_SORT ) ) {
      pg->Refresh();
    } else
    { pg->DrawItem( p ); }
  }
}

bool wxPropertyGridInterface::SetPropertyMaxLength( wxPGPropArg id, int maxLen ) {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  wxPropertyGrid* pg = m_pState->GetGrid();
  p->m_maxLen = ( short ) maxLen;
  if( pg == p->GetGrid() && p == m_pState->GetSelection() ) {
    wxWindow* wnd = pg->GetEditorControl();
    wxTextCtrl* tc = wxDynamicCast( wnd, wxTextCtrl );
    if( tc ) {
      tc->SetMaxLength( maxLen );
    } else
    { return false; }
  }
  return true;
}



#define IMPLEMENT_GET_VALUE(T,TRET,BIGNAME,DEFRETVAL) \
  TRET wxPropertyGridInterface::GetPropertyValueAs##BIGNAME( wxPGPropArg id ) const \
  { \
    wxPG_PROP_ARG_CALL_PROLOG_RETVAL(DEFRETVAL) \
    wxVariant value = p->GetValue(); \
    if ( wxStrcmp(value.GetType(), wxPGTypeName_##T) != 0 ) \
    { \
      wxPGGetFailed(p,wxPGTypeName_##T); \
      return (TRET)DEFRETVAL; \
    } \
    return (TRET)value.Get##BIGNAME(); \
  }


wxString wxPropertyGridInterface::GetPropertyValueAsString( wxPGPropArg id ) const {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( wxEmptyString )
  return p->GetValueAsString( wxPG_FULL_VALUE );
}

bool wxPropertyGridInterface::GetPropertyValueAsBool( wxPGPropArg id ) const {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  wxVariant value = p->GetValue();
  if( wxStrcmp( value.GetType(), wxPGTypeName_bool ) == 0 ) {
    return value.GetBool();
  }
  if( wxStrcmp( value.GetType(), wxPGTypeName_long ) == 0 ) {
    return value.GetLong() ? true : false;
  }
  wxPGGetFailed( p, wxPGTypeName_bool );
  return false;
}

IMPLEMENT_GET_VALUE( long, long, Long, 0 )
IMPLEMENT_GET_VALUE( double, double, Double, 0.0 )
IMPLEMENT_GET_VALUE( void, void*, VoidPtr, NULL )

wxObject* wxPropertyGridInterface::GetPropertyValueAsWxObjectPtr( wxPGPropArg id ) const {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( ( wxObject* )NULL )
  wxVariant variant = p->GetValue();
  wxObject* result;
  if( wxPGVariantToWxObjectPtr( variant, &result ) ) {
    return result;
  }
  return NULL;
}

bool wxPropertyGridInterface::IsPropertyExpanded( wxPGPropArg id ) const {
  wxPG_PROP_ARG_CALL_PROLOG_RETVAL( false )
  return p->IsExpanded();
}


wxPGProperty* wxPropertyGridInterface::CreatePropertyByType( const wxString &,
    const wxString &,
    const wxString & ) {
  return ( wxPGProperty* ) NULL;
}


wxPGProperty* wxPropertyGridInterface::CreatePropertyByClass( const wxString &,
    const wxString &,
    const wxString & ) {
  return ( wxPGProperty* ) NULL;
}



bool wxPropertyGridInterface::ChangePropertyValue( wxPGPropArg id, wxVariant newValue ) {
  return GetPropertyGrid()->ChangePropertyValue( id, newValue );
}



void wxPropertyGridIteratorBase::Init( wxPropertyGridState* state, int flags, wxPGProperty* property, int dir ) {
  wxASSERT( dir == 1 || dir == -1 );
  m_state = state;
  m_baseParent = state->DoGetRoot();
  if( !property && m_baseParent->GetChildCount() ) {
    property = m_baseParent->Item( 0 );
  }
  m_property = property;
  wxPG_ITERATOR_CREATE_MASKS( flags, m_itemExMask, m_parentExMask )
  if( property && ( property->GetFlags() & m_itemExMask ) ) {
    if( dir == 1 ) {
      Next();
    } else
    { Prev(); }
  }
}

void wxPropertyGridIteratorBase::Init( wxPropertyGridState* state, int flags, int startPos, int dir ) {
  wxPGProperty* property;
  if( startPos == wxTOP ) {
    property = NULL;
    if( dir == 0 ) {
      dir = 1;
    }
  } else if( startPos == wxBOTTOM ) {
    property = state->GetLastItem( flags );
    if( dir == 0 ) {
      dir = -1;
    }
  } else {
    wxASSERT_MSG( false, _T( "Only supported stating positions are wxTOP and wxBOTTOM" ) );
    property = NULL;
  }
  Init( state, flags, property, dir );
}

void wxPropertyGridIteratorBase::Assign( const wxPropertyGridIteratorBase& it ) {
  m_property = it.m_property;
  m_state = it.m_state;
  m_baseParent = it.m_baseParent;
  m_itemExMask = it.m_itemExMask;
  m_parentExMask = it.m_parentExMask;
}

void wxPropertyGridIteratorBase::Prev() {
  wxPGProperty* property = m_property;
  if( !property ) {
    return;
  }
  wxPGProperty* parent = property->GetParent();
  wxASSERT( parent );
  unsigned int index = property->GetIndexInParent();
  if( index > 0 ) {
    index--;
    property = parent->Item( index );
    if( property->GetChildCount() &&
        wxPG_ITERATOR_PARENTEXMASK_TEST( property, m_parentExMask ) ) {
      property = property->Last();
    }
  } else {
    if( parent == m_baseParent ) {
      m_property = NULL;
      return;
    } else
    { property = parent; }
  }
  m_property = property;
  if( property->GetFlags() & m_itemExMask ) {
    Prev();
  }
}

void wxPropertyGridIteratorBase::Next( bool iterateChildren ) {
  wxPGProperty* property = m_property;
  if( !property ) {
    return;
  }
  if( property->GetChildCount() &&
      wxPG_ITERATOR_PARENTEXMASK_TEST( property, m_parentExMask ) &&
      iterateChildren ) {
    property = property->Item( 0 );
  } else {
    wxPGProperty* parent = property->GetParent();
    wxASSERT( parent );
    unsigned int index = property->GetIndexInParent() + 1;
    if( index < parent->GetChildCount() ) {
      property = parent->Item( index );
    } else {
      if( parent == m_baseParent ) {
        m_property = NULL;
      } else {
        m_property = parent;
        Next( false );
      }
      return;
    }
  }
  m_property = property;
  if( property->GetFlags() & m_itemExMask ) {
    Next();
  }
}




class wxPGVIteratorBase_State : public wxPGVIteratorBase {
  public:
    wxPGVIteratorBase_State( wxPropertyGridState* state, int flags ) {
      m_it.Init( state, flags );
    }
    virtual ~wxPGVIteratorBase_State() { }
    virtual void Next() { m_it.Next(); }
};

wxPGVIterator wxPropertyGridInterface::GetVIterator( int flags ) const {
  return wxPGVIterator( new wxPGVIteratorBase_State( m_pState, flags ) );
}



static wxString EscapeDelimiters( const wxString& s ) {
  wxString result;
  result.Alloc( s.length() );
  const wxChar* ch = s.c_str();
  while( *ch ) {
    if( *ch == _T( ';' ) || *ch == _T( '|' ) || *ch == _T( ',' ) ) {
      result += _T( '\\' );
    }
    result += *ch;
    ++ch;
  }
  return result;
}

wxString wxPropertyGridInterface::SaveEditableState( int includedStates ) const {
  wxString result;
  size_t pageIndex = 0;
  wxPropertyGridState* pageStateIdx = GetPageState( pageIndex );
  wxArrayPtrVoid pageStates;
  while( pageStateIdx ) {
    pageStates.Add( pageStateIdx );
    pageIndex += 1;
    pageStateIdx = GetPageState( pageIndex );
  }
  for( pageIndex = 0; pageIndex < pageStates.size(); pageIndex++ ) {
    wxPropertyGridState* pageState = ( wxPropertyGridState* ) pageStates[pageIndex];
    if( includedStates & SelectionState ) {
      wxString sel;
      if( pageState->GetSelection() ) {
        sel = pageState->GetSelection()->GetName();
      }
      result += _T( "selection=" );
      result += EscapeDelimiters( sel );
      result += _T( ";" );
    }
    if( includedStates & ExpandedState ) {
      wxArrayPGProperty ptrs;
      wxPropertyGridConstIterator it =
        wxPropertyGridConstIterator( pageState,
                                     wxPG_ITERATE_ALL_PARENTS_RECURSIVELY | wxPG_ITERATE_HIDDEN,
                                     wxNullProperty );
      result += _T( "expanded=" );
      for( ;
           !it.AtEnd();
           it.Next() ) {
        const wxPGProperty* p = it.GetProperty();
        if( !p->HasFlag( wxPG_PROP_COLLAPSED ) ) {
          result += EscapeDelimiters( p->GetName() );
          result += _T( "," );
        }
      }
      if( result.Last() == _T( ',' ) ) {
        result.RemoveLast();
      }
      result += _T( ";" );
    }
    if( includedStates & ScrollPosState ) {
      int x, y;
      GetPropertyGrid()->GetViewStart( &x, &y );
      result += wxString::Format( _T( "scrollpos=%i,%i;" ), x, y );
    }
    if( includedStates & SplitterPosState ) {
      result += _T( "splitterpos=" );
      for( size_t i = 0; i < pageState->GetColumnCount(); i++ ) {
        result += wxString::Format( _T( "%i," ), pageState->DoGetSplitterPosition( i ) );
      }
      result.RemoveLast();
      result += _T( ";" );
    }
    if( includedStates & PageState ) {
      result += _T( "ispageselected=" );
      if( GetPageState( -1 ) == pageState ) {
        result += _T( "1;" );
      } else
      { result += _T( "0;" ); }
    }
    if( includedStates & DescBoxState ) {
      wxVariant v = GetEditableStateItem( _T( "descboxheight" ) );
      if( !v.IsNull() ) {
        result += wxString::Format( _T( "descboxheight=%i;" ), ( int )v.GetLong() );
      }
    }
    result.RemoveLast();
    result += _T( "|" );
  }
  if( result.length() ) {
    result.RemoveLast();
  }
  return result;
}

bool wxPropertyGridInterface::RestoreEditableState( const wxString& src, int restoreStates ) {
  wxPropertyGrid* pg = GetPropertyGrid();
  wxPGProperty* newSelection = NULL;
  size_t pageIndex;
  long vx = -1;
  long vy = -1;
  long selectedPage = -1;
  bool pgSelectionSet = false;
  bool res = true;
  pg->Freeze();
  wxArrayString pageStrings = ::wxSplit( src, _T( '|' ), _T( '\\' ) );
  for( pageIndex = 0; pageIndex < pageStrings.size(); pageIndex++ ) {
    wxPropertyGridState* pageState = GetPageState( pageIndex );
    if( !pageState ) {
      break;
    }
    wxArrayString kvpairStrings = ::wxSplit( pageStrings[pageIndex], _T( ';' ), _T( '\\' ) );
    for( size_t i = 0; i < kvpairStrings.size(); i++ ) {
      const wxString& kvs = kvpairStrings[i];
      int eq_pos = kvs.Find( _T( '=' ) );
      if( eq_pos != wxNOT_FOUND ) {
        wxString key = kvs.substr( 0, eq_pos );
        wxString value = kvs.substr( eq_pos + 1 );
        wxArrayString values = ::wxSplit( value, _T( ',' ), _T( '\\' ) );
        if( key == _T( "expanded" ) ) {
          if( restoreStates & ExpandedState ) {
            wxPropertyGridIterator it =
              wxPropertyGridIterator( pageState,
                                      wxPG_ITERATE_ALL,
                                      wxNullProperty );
            for( ; !it.AtEnd(); it.Next() ) {
              wxPGProperty* p = it.GetProperty();
              pageState->DoCollapse( p );
            }
            for( size_t n = 0; n < values.size(); n++ ) {
              const wxString& name = values[n];
              wxPGProperty* prop = GetPropertyByName( name );
              if( prop )
              { pageState->DoExpand( prop ); }
            }
          }
        } else if( key == _T( "scrollpos" ) ) {
          if( restoreStates & ScrollPosState ) {
            if( values.size() == 2 ) {
              values[0].ToLong( &vx );
              values[1].ToLong( &vy );
            } else
            { res = false; }
          }
        } else if( key == _T( "splitterpos" ) ) {
          if( restoreStates & SplitterPosState ) {
            for( size_t n = 1; n < values.size(); n++ ) {
              long pos = 0;
              values[n].ToLong( &pos );
              if( pos > 0 )
              { pageState->DoSetSplitterPosition( pos, n ); }
            }
          }
        } else if( key == _T( "selection" ) ) {
          if( restoreStates & SelectionState ) {
            if( values.size() > 0 ) {
              if( pageState->IsDisplayed() ) {
                if( values[0].length() )
                { newSelection = GetPropertyByName( value ); }
                pgSelectionSet = true;
              } else {
                if( values[0].length() )
                { pageState->DoSetSelection( GetPropertyByName( value ) ); }
                else
                { pageState->DoClearSelection(); }
              }
            }
          }
        } else if( key == _T( "ispageselected" ) ) {
          if( restoreStates & PageState ) {
            long pageSelStatus;
            if( values.size() == 1 && values[0].ToLong( &pageSelStatus ) ) {
              if( pageSelStatus )
              { selectedPage = pageIndex; }
            } else
            { res = false; }
          }
        } else if( key == _T( "descboxheight" ) ) {
          if( restoreStates & DescBoxState ) {
            long descBoxHeight;
            if( values.size() == 1 && values[0].ToLong( &descBoxHeight ) )
            { SetEditableStateItem( _T( "descboxheight" ), descBoxHeight ); }
            else
            { res = false; }
          }
        } else
        { res = false; }
      }
    }
  }
  pageIndex = 0;
  wxPropertyGridState* pageState = GetPageState( pageIndex );
  while( pageState ) {
    pageState->VirtualHeightChanged();
    pageIndex += 1;
    pageState = GetPageState( pageIndex );
  }
  pg->Thaw();
  if( pgSelectionSet ) {
    if( newSelection ) {
      pg->SelectProperty( newSelection );
    } else
    { pg->ClearSelection(); }
  }
  if( selectedPage != -1 ) {
    DoSelectPage( selectedPage );
  }
  if( vx >= 0 ) {
    pg->Scroll( vx, vy );
  }
  return res;
}

wxPGProperty* wxPropertyGridState::GetLastItem( int flags ) {
  if( !m_properties->GetCount() ) {
    return ( wxPGProperty* ) NULL;
  }
  wxPG_ITERATOR_CREATE_MASKS( flags, int itemExMask, int parentExMask )
  wxPGProperty* pwc = ( wxPGProperty* )m_properties->Last();
  while( pwc->GetChildCount() &&
         wxPG_ITERATOR_PARENTEXMASK_TEST( pwc, parentExMask ) ) {
    pwc = ( wxPGProperty* ) pwc->Last();
  }
  if( pwc->GetFlags() & itemExMask ) {
    wxPropertyGridIterator it( this, flags, pwc );
    for( ; !it.AtEnd(); it.Prev() );
    pwc = ( wxPGProperty* ) it.GetProperty();
  }
  return pwc;
}

wxPropertyCategory* wxPropertyGridState::GetPropertyCategory( const wxPGProperty* p ) const {
  const wxPGProperty* parent = ( const wxPGProperty* )p;
  const wxPGProperty* grandparent = ( const wxPGProperty* )parent->GetParent();
  do {
    parent = grandparent;
    grandparent = ( wxPGProperty* )parent->GetParent();
    if( parent->IsCategory() && grandparent ) {
      return ( wxPropertyCategory* )parent;
    }
  } while( grandparent );
  return ( wxPropertyCategory* ) NULL;
}



wxPGProperty* wxPropertyGridState::GetPropertyByLabel( const wxString& label,
    wxPGProperty* parent ) const {
  size_t i;
  if( !parent ) {
    parent = ( wxPGProperty* ) &m_regularArray;
  }
  for( i = 0; i < parent->GetCount(); i++ ) {
    wxPGProperty* p = parent->Item( i );
    if( p->m_label == label ) {
      return p;
    }
    if( p->GetChildCount() ) {
      p = GetPropertyByLabel( label, ( wxPGProperty* )p );
      if( p ) {
        return p;
      }
    }
  }
  return NULL;
}


wxPGProperty* wxPropertyGridState::BaseGetPropertyByName( wxPGPropNameStr name ) const {
  wxPGHashMapS2P::const_iterator it;
  it = m_dictName.find( name );
  if( it != m_dictName.end() ) {
    return ( wxPGProperty* ) it->second;
  }
  return ( wxPGProperty* ) NULL;
}



bool wxPropertyGridState::EnableCategories( bool enable ) {
  ITEM_ITERATION_VARIABLES
  if( enable ) {
    if( !IsInNonCatMode() ) {
      return false;
    }
    m_properties = &m_regularArray;
    ITEM_ITERATION_INIT_FROM_THE_TOP
    ITEM_ITERATION_LOOP_BEGIN
    p->m_arrIndex = i;
    p->m_parent = parent;
    if( parent->IsCategory() &&
        !p->IsCategory() ) {
      p->m_depth = parent->m_depth;
    } else
    { p->m_depth = parent->m_depth + 1; }
    ITEM_ITERATION_LOOP_END
  } else {
    if( IsInNonCatMode() ) {
      return false;
    }
    if( !m_abcArray ) {
      InitNonCatMode();
    }
    m_properties = m_abcArray;
    ITEM_ITERATION_INIT_FROM_THE_TOP
    ITEM_ITERATION_LOOP_BEGIN
    p->m_arrIndex = i;
    p->m_parent = parent;
    p->m_depth = parent->m_depth + 1;
    ITEM_ITERATION_LOOP_END
  }
  VirtualHeightChanged();
  if( m_pPropGrid->GetState() == this ) {
    m_pPropGrid->RecalculateVirtualSize();
  }
  return true;
}


static int wxPG_SortFunc_ByFunction( void **p1, void **p2 ) {
  wxPGProperty *pp1 = *( ( wxPGProperty** )p1 );
  wxPGProperty *pp2 = *( ( wxPGProperty** )p2 );
  wxPropertyGrid* pg = pp1->GetGrid();
  wxPGSortCallback sortFunction = pg->GetSortFunction();
  return sortFunction( pg, pp1, pp2 );
}

static int wxPG_SortFunc_ByLabel( void **p1, void **p2 ) {
  wxPGProperty *pp1 = *( ( wxPGProperty** )p1 );
  wxPGProperty *pp2 = *( ( wxPGProperty** )p2 );
  return pp1->GetLabel().CmpNoCase( pp2->GetLabel() );
}

void wxPropertyGridState::Sort( wxPGProperty* p ) {
  if( !p ) {
    p = ( wxPGProperty* )m_properties;
  }
  if( !p->GetChildCount() ) {
    return;
  }
  wxPGProperty* pwc = ( wxPGProperty* )p;
  if( pwc->m_children.GetCount() < 1 ) {
    return;
  }
  if( GetGrid()->GetSortFunction() ) {
    pwc->m_children.Sort( wxPG_SortFunc_ByFunction );
  } else {
    pwc->m_children.Sort( wxPG_SortFunc_ByLabel );
  }
  pwc->FixIndexesOfChildren();
}


void wxPropertyGridState::Sort() {
  Sort( m_properties );
  if( !IsInNonCatMode() ) {
    size_t i;
    for( i = 0; i < m_properties->GetCount(); i++ ) {
      wxPGProperty* p = m_properties->Item( i );
      if( p->IsCategory() ) {
        Sort( p );
      }
    }
  }
}



wxPGProperty* wxPropertyGridState::DoGetItemAtY( int y ) const {
  if( y < 0 ) {
    return ( wxPGProperty* ) NULL;
  }
  unsigned int a = 0;
  return m_properties->GetItemAtY( y, GetGrid()->m_lineHeight, &a );
}


wxPropertyGridHitTestResult wxPropertyGridState::HitTest( const wxPoint&pt ) const {
  wxPropertyGridHitTestResult result;
  result.column = HitTestH( pt.x, &result.splitter, &result.splitterHitOffset );
  result.property = DoGetItemAtY( pt.y );
  return result;
}



int wxPropertyGridState::GetColumnFitWidth( wxClientDC& dc,
    wxPGProperty* pwc,
    unsigned int col,
    bool subProps ) const {
  wxPropertyGrid* pg = m_pPropGrid;
  size_t i;
  int maxW = 0;
  int w, h;
  for( i = 0; i < pwc->GetCount(); i++ ) {
    wxPGProperty* p = pwc->Item( i );
    if( !p->IsCategory() ) {
      dc.GetTextExtent( p->GetColumnText( col ), &w, &h );
      if( col == 0 ) {
        w += ( ( ( int )p->m_depth - 1 ) * pg->m_subgroup_extramargin );
      }
      if( col == 1 ) {
        w += p->GetImageOffset( pg->GetImageRect( p, -1 ).GetWidth() );
      }
      w += ( wxPG_XBEFORETEXT * 2 );
      if( w > maxW ) {
        maxW = w;
      }
    }
    if( p->GetChildCount() &&
        ( subProps || p->IsCategory() ) ) {
      w = GetColumnFitWidth( dc, p, col, subProps );
      if( w > maxW ) {
        maxW = w;
      }
    }
  }
  return maxW;
}


int wxPropertyGridState::GetColumnFullWidth( wxClientDC &dc, wxPGProperty *p, unsigned int col ) {
  if( p->IsCategory() ) {
    return 0;
  }
  long w, h;
  dc.GetTextExtent( p->GetColumnText( col ), &w, &h );
  if( col == 0 ) {
    w += ( int )p->m_depth * m_pPropGrid->m_subgroup_extramargin;
  }
  if( col == 1 ) {
    w += p->GetImageOffset( m_pPropGrid->GetImageRect( p, -1 ).GetWidth() );
  }
  w += ( wxPG_XBEFORETEXT * 2 );
  return w;
}


int wxPropertyGridState::DoGetSplitterPosition( int splitterColumn ) const {
  int n = GetGrid()->m_marginWidth;
  int i;
  for( i = 0; i <= splitterColumn; i++ ) {
    n += m_colWidths[i];
  }
  return n;
}

int wxPropertyGridState::GetColumnMinWidth( int ( column ) ) const {
  return wxPG_DRAG_MARGIN;
}

void wxPropertyGridState::PropagateColSizeDec( int column, int decrease, int dir ) {
  int origWidth = m_colWidths[column];
  m_colWidths[column] -= decrease;
  int min = GetColumnMinWidth( column );
  int more = 0;
  if( m_colWidths[column] < min ) {
    more = decrease - ( origWidth - min );
    m_colWidths[column] = min;
  }
  if( m_colWidths.size() <= 2 ) {
    return;
  }
  column += dir;
  if( more && column < ( int )m_colWidths.size() && column >= 0 ) {
    PropagateColSizeDec( column, more, dir );
  }
}

void wxPropertyGridState::DoSetSplitterPosition( int newXPos, int splitterColumn, bool ( allPages ), bool fromAutoCenter ) {
  wxPropertyGrid* pg = GetGrid();
  int adjust = newXPos - DoGetSplitterPosition( splitterColumn );
  if( !pg->HasVirtualWidth() ) {
    int otherColumn;
    if( adjust > 0 ) {
      otherColumn = splitterColumn + 1;
      if( otherColumn == ( int )m_colWidths.size() ) {
        otherColumn = 0;
      }
      m_colWidths[splitterColumn] += adjust;
      PropagateColSizeDec( otherColumn, adjust, 1 );
    } else {
      otherColumn = splitterColumn + 1;
      if( otherColumn == ( int )m_colWidths.size() ) {
        otherColumn = 0;
      }
      m_colWidths[otherColumn] -= adjust;
      PropagateColSizeDec( splitterColumn, -adjust, -1 );
    }
  } else {
    m_colWidths[splitterColumn] += adjust;
  }
  if( splitterColumn == 0 ) {
    m_fSplitterX = ( double ) newXPos;
  }
  if( !fromAutoCenter ) {
    if( pg->GetState() == this ) {
      pg->SetInternalFlag( wxPG_FL_SPLITTER_PRE_SET );
    }
    CheckColumnWidths();
  }
}

void wxPropertyGridState::SetSplitterLeft( bool subProps ) {
  wxPropertyGrid* pg = GetGrid();
  wxClientDC dc( pg );
  dc.SetFont( pg->GetFont() );
  int maxW = GetColumnFitWidth( dc, m_properties, 0, subProps );
  if( maxW > 0 ) {
    maxW += pg->m_marginWidth;
    DoSetSplitterPosition( maxW );
  }
  pg->SetInternalFlag( wxPG_FL_DONT_CENTER_SPLITTER );
}

wxSize wxPropertyGridState::DoFitColumns( bool ( allowGridResize ) ) {
  wxPropertyGrid* pg = GetGrid();
  wxClientDC dc( pg );
  dc.SetFont( pg->GetFont() );
  int marginWidth = pg->m_marginWidth;
  int accWid = marginWidth;
  int maxColWidth = 500;
  for( unsigned int col = 0; col < GetColumnCount(); col++ ) {
    int fitWid = GetColumnFitWidth( dc, m_properties, col, true );
    int colMinWidth = GetColumnMinWidth( col );
    if( fitWid < colMinWidth ) {
      fitWid = colMinWidth;
    } else if( fitWid > maxColWidth ) {
      fitWid = maxColWidth;
    }
    m_colWidths[col] = fitWid;
    accWid += fitWid;
  }
  int remaining = m_width - accWid;
  m_colWidths[GetColumnCount() - 1] += remaining;
  pg->SetInternalFlag( wxPG_FL_DONT_CENTER_SPLITTER );
  int firstSplitterX = marginWidth + m_colWidths[0];
  m_fSplitterX = ( double ) firstSplitterX;
  if( pg->GetState() == this ) {
    pg->SetSplitterPosition( firstSplitterX, false );
    pg->Refresh();
  }
  int x, y;
  pg->GetVirtualSize( &x, &y );
  return wxSize( accWid, y );
}

void wxPropertyGridState::CheckColumnWidths( int widthChange ) {
  if( m_width == 0 ) {
    return;
  }
  wxPropertyGrid* pg = GetGrid();
  #ifdef __WXDEBUG__
  const bool debug = false;
  #endif
  unsigned int i;
  unsigned int lastColumn = m_colWidths.size() - 1;
  int width = m_width;
  int clientWidth = pg->GetClientSize().x;
  int reduceCol = -1;
  #ifdef __WXDEBUG__
  if( debug ) {
    wxLogDebug( _T( "ColumnWidthCheck (virtualWidth: %i, clientWidth: %i)" ), width, clientWidth );
  }
  #endif
  for( i = 0; i < m_colWidths.size(); i++ ) {
    int min = GetColumnMinWidth( i );
    if( m_colWidths[i] <= min ) {
      m_colWidths[i] = min;
    } else
    { reduceCol = i; }
  }
  int colsWidth = pg->m_marginWidth;
  for( i = 0; i < m_colWidths.size(); i++ ) {
    colsWidth += m_colWidths[i];
  }
  #ifdef __WXDEBUG__
  if( debug ) {
    wxLogDebug( _T( "  HasVirtualWidth: %i  colsWidth: %i" ), ( int )pg->HasVirtualWidth(), colsWidth );
  }
  #endif
  if( !pg->HasVirtualWidth() ) {
    int widthHigher = width - colsWidth;
    if( colsWidth < width ) {
      #ifdef __WXDEBUG__
      if( debug ) {
        wxLogDebug( _T( "  Adjust last column to %i" ), m_colWidths[lastColumn] + widthHigher );
      }
      #endif
      m_colWidths[lastColumn] = m_colWidths[lastColumn] + widthHigher;
    } else if( colsWidth > width ) {
      if( reduceCol != -1 ) {
        #ifdef __WXDEBUG__
        if( debug ) {
          wxLogDebug( _T( "  Reduce column %i (by %i)" ), reduceCol, -widthHigher );
        }
        #endif
        m_colWidths[reduceCol] = m_colWidths[reduceCol] + widthHigher;
        CheckColumnWidths();
      }
    }
  } else {
    if( colsWidth < clientWidth ) {
      m_colWidths[lastColumn] = m_colWidths[lastColumn] + ( clientWidth - colsWidth );
    }
    m_width = colsWidth;
    if( pg->GetState() == this ) {
      pg->RecalculateVirtualSize();
    }
  }
  #ifdef __WXDEBUG__
  if( debug )
    for( i = 0; i < m_colWidths.size(); i++ ) {
      wxLogDebug( _T( "col%i: %i" ), i, m_colWidths[i] );
    }
  #endif
  if( !( pg->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER ) ) {
    if( m_colWidths.size() == 2 &&
        m_columnProportions[0] == m_columnProportions[1] ) {
      float centerX = ( float )( pg->m_width / 2 );
      float splitterX;
      if( m_fSplitterX < 0.0 ) {
        #ifdef __WXDEBUG__
        if( debug ) {
          wxLogDebug( _T( "  auto-center splitter reset" ) );
        }
        #endif
        splitterX = centerX;
      } else if( widthChange ) {
        #ifdef __WXDEBUG__
        if( debug )
          wxLogDebug( _T( "  auto-center with widthChange=%i" ),
                      widthChange );
        #endif
        splitterX = m_fSplitterX + ( float( widthChange ) * 0.5 );
        float deviation = fabs( centerX - splitterX );
        if( deviation > 20.0 ) {
          if( splitterX > centerX ) {
            splitterX -= 2;
          } else
          { splitterX += 2; }
        }
      } else {
        splitterX = m_fSplitterX;
        float deviation = fabs( centerX - splitterX );
        #ifdef __WXDEBUG__
        if( debug )
          wxLogDebug( _T( "  auto-center with deviation=%.2f" ),
                      deviation );
        #endif
        if( deviation > 50.0 ) {
          splitterX = centerX;
        }
      }
      DoSetSplitterPosition( ( int )splitterX, 0, false, true );
      m_fSplitterX = splitterX;
    } else
    { ResetColumnSizes( true ); }
  }
}

void wxPropertyGridState::ResetColumnSizes( bool fromAutoCenter ) {
  unsigned int i;
  int psum = 0;
  for( i = 0; i < m_colWidths.size(); i++ ) {
    psum += m_columnProportions[i];
  }
  int puwid = ( m_pPropGrid->m_width * 256 ) / psum;
  int cpos = 0;
  for( i = 0; i < ( m_colWidths.size() - 1 ); i++ ) {
    int cwid = ( puwid * m_columnProportions[i] ) / 256;
    cpos += cwid;
    DoSetSplitterPosition( cpos, i, false, fromAutoCenter );
  }
}

void wxPropertyGridState::SetColumnCount( int colCount ) {
  wxASSERT( colCount >= 2 );
  m_colWidths.SetCount( colCount, wxPG_DRAG_MARGIN );
  m_columnProportions.SetCount( colCount, 1 );
  if( m_colWidths.size() > ( unsigned int )colCount )
    m_colWidths.RemoveAt( m_colWidths.size() - 1,
                          m_colWidths.size() - colCount );
  if( m_pPropGrid->GetState() == this ) {
    m_pPropGrid->RecalculateVirtualSize();
  } else {
    CheckColumnWidths();
  }
}

void wxPropertyGridState::DoSetColumnProportion( unsigned int column,
    int proportion ) {
  wxASSERT_MSG( proportion >= 1,
                _T( "Column proportion must 1 or higher" ) );
  if( proportion < 1 ) {
    proportion = 1;
  }
  while( m_columnProportions.size() <= column ) {
    m_columnProportions.push_back( 1 );
  }
  m_columnProportions[column] = proportion;
}


int wxPropertyGridState::HitTestH( int x, int* pSplitterHit, int* pSplitterHitOffset ) const {
  int cx = GetGrid()->m_marginWidth;
  int col = -1;
  int prevSplitter = -1;
  while( x > cx ) {
    col++;
    if( col >= ( int )m_colWidths.size() ) {
      *pSplitterHit = -1;
      return col;
    }
    prevSplitter = cx;
    cx += m_colWidths[col];
  }
  if( col >= 1 ) {
    int diff = x - prevSplitter;
    if( abs( diff ) < wxPG_SPLITTERX_DETECTMARGIN1 ) {
      *pSplitterHit = col - 1;
      *pSplitterHitOffset = diff;
      return col;
    }
  }
  int nextSplitter = cx;
  if( col < ( int )( m_colWidths.size() - 1 ) ) {
    int diff = x - nextSplitter;
    if( abs( diff ) < wxPG_SPLITTERX_DETECTMARGIN1 ) {
      *pSplitterHit = col;
      *pSplitterHitOffset = diff;
      return col;
    }
  }
  *pSplitterHit = -1;
  return col;
}

bool wxPropertyGridState::ArePropertiesAdjacent( wxPGProperty* prop1,
    wxPGProperty* prop2,
    int iterFlags ) const {
  const wxPGProperty* ap1 =
    wxPropertyGridConstIterator::OneStep( this,
                                          iterFlags,
                                          prop1,
                                          1 );
  if( ap1 && ap1 == prop2 ) {
    return true;
  }
  const wxPGProperty* ap2 =
    wxPropertyGridConstIterator::OneStep( this,
                                          iterFlags,
                                          prop1,
                                          -1 );
  if( ap2 && ap2 == prop2 ) {
    return true;
  }
  return false;
}

bool wxPropertyGridInterface::SetColumnProportion( unsigned int column,
    int proportion ) {
  wxCHECK( m_pState, false );
  wxPropertyGrid* pg = m_pState->GetGrid();
  wxCHECK( pg, false );
  wxCHECK( pg->HasFlag( wxPG_SPLITTER_AUTO_CENTER ), false );
  m_pState->DoSetColumnProportion( column, proportion );
  return true;
}



bool wxPropertyGridState::DoSetPropertyValueString( wxPGProperty* p, const wxString& value ) {
  if( p ) {
    int flags = wxPG_REPORT_ERROR | wxPG_FULL_VALUE | wxPG_PROGRAMMATIC_VALUE;
    wxVariant variant = p->GetValueRef();
    bool res;
    if( p->GetMaxLength() <= 0 ) {
      res = p->ActualStringToValue( variant, value, flags );
    } else
    { res = p->ActualStringToValue( variant, value.Mid( 0, p->GetMaxLength() ), flags ); }
    if( res ) {
      p->SetValue( variant );
    }
    return true;
  }
  return false;
}


bool wxPropertyGridState::DoSetPropertyValue( wxPGProperty* p, wxVariant& value ) {
  if( p ) {
    p->SetValue( value );
    return true;
  }
  return false;
}


bool wxPropertyGridState::DoSetPropertyValueWxObjectPtr( wxPGProperty* p, wxObject* value ) {
  if( p ) {
    {
      wxVariant v( value );
      DoSetPropertyValue( p, v );
      return true;
    }
  }
  return false;
}



bool wxPropertyGridState::DoIsPropertySelected( wxPGProperty* prop ) const {
  const wxArrayPGProperty& selection = m_selection;
  for( unsigned int i = 0; i < selection.size(); i++ ) {
    if( selection[i] == prop ) {
      return true;
    }
  }
  return false;
}


void wxPropertyGridState::DoRemoveFromSelection( wxPGProperty* prop ) {
  for( unsigned int i = 0; i < m_selection.size(); i++ ) {
    if( m_selection[i] == prop ) {
      wxPropertyGrid* pg = m_pPropGrid;
      if( i == 0 && pg ->GetState() == this ) {
        wxArrayPGProperty sel = m_selection;
        sel.erase( sel.begin() + i );
        wxPGProperty* newFirst;
        if( sel.size() ) {
          newFirst = sel[0];
        } else
        { newFirst = NULL; }
        pg->DoSelectProperty( newFirst,
                              wxPG_SEL_DONT_SEND_EVENT );
        m_selection = sel;
        pg->Refresh();
      } else
      { m_selection.erase( m_selection.begin() + i ); }
      return;
    }
  }
}


void wxPropertyGridState::ClearModifiedStatus( wxPGProperty* p ) {
  if( p->m_flags & wxPG_PROP_MODIFIED ) {
    p->m_flags &= ~( wxPG_PROP_MODIFIED );
    if( m_pPropGrid->GetState() == this ) {
      if( p == GetSelection() ) {
        m_pPropGrid->RefreshEditor();
      }
      m_pPropGrid->DrawItem( p );
    }
  }
  size_t i;
  for( i = 0; i < p->GetChildCount(); i++ ) {
    ClearModifiedStatus( p->Item( i ) );
  }
}


bool wxPropertyGridState::DoCollapse( wxPGProperty* p ) {
  wxCHECK_MSG( p, false, _T( "invalid property id" ) );
  wxPGProperty* pwc = ( wxPGProperty* )p;
  if( !pwc->GetChildCount() ) {
    return false;
  }
  if( !pwc->IsExpanded() ) {
    return false;
  }
  pwc->SetExpanded( false );
  VirtualHeightChanged();
  return true;
}


bool wxPropertyGridState::DoExpand( wxPGProperty* p ) {
  wxCHECK_MSG( p, false, _T( "invalid property id" ) );
  wxPGProperty* pwc = ( wxPGProperty* )p;
  if( !pwc->GetChildCount() ) {
    return false;
  }
  if( pwc->IsExpanded() ) {
    return false;
  }
  pwc->SetExpanded( true );
  VirtualHeightChanged();
  return true;
}


bool wxPropertyGridState::DoSelectProperty( wxPGProperty* p, unsigned int flags ) {
  if( this == m_pPropGrid->GetState() ) {
    return m_pPropGrid->DoSelectProperty( p, flags );
  }
  DoSetSelection( p );
  return true;
}


bool wxPropertyGridState::DoHideProperty( wxPGProperty* p, bool hide, int flags ) {
  p->DoHide( hide, flags );
  VirtualHeightChanged();
  return true;
}


bool wxPropertyGridState::DoEnableProperty( wxPGProperty* p, bool enable ) {
  if( p ) {
    if( enable ) {
      if( !( p->m_flags & wxPG_PROP_DISABLED ) ) {
        return false;
      }
      p->m_flags &= ~( wxPG_PROP_DISABLED );
    } else {
      if( p->m_flags & wxPG_PROP_DISABLED ) {
        return false;
      }
      p->m_flags |= wxPG_PROP_DISABLED;
    }
    unsigned int i;
    for( i = 0; i < p->GetChildCount(); i++ ) {
      DoEnableProperty( p->Item( i ), enable );
    }
    return true;
  }
  return false;
}




wxVariant wxPropertyGridState::DoGetPropertyValues( const wxString& listname,
    wxPGProperty* baseparent,
    long flags ) const {
  wxPGProperty* pwc = ( wxPGProperty* ) baseparent;
  if( !pwc ) {
    pwc = m_properties;
  }
  wxVariantList tempList;
  wxVariant v( tempList, listname );
  if( pwc->GetChildCount() ) {
    if( flags & wxPG_KEEP_STRUCTURE ) {
      wxASSERT( !pwc->IsFlagSet( wxPG_PROP_AGGREGATE ) );
      size_t i;
      for( i = 0; i < pwc->GetCount(); i++ ) {
        wxPGProperty* p = pwc->Item( i );
        if( !p->GetChildCount() || p->HasFlag( wxPG_PROP_AGGREGATE ) ) {
          wxVariant variant = p->GetValue();
          variant.SetName( p->GetBaseName() );
          v.Append( variant );
        } else
        { v.Append( DoGetPropertyValues( p->m_name, p, flags | wxPG_KEEP_STRUCTURE ) ); }
        if( ( flags & wxPG_INC_ATTRIBUTES ) && p->m_attributes.GetCount() ) {
          v.Append( p->GetAttributesAsList() );
        }
      }
    } else {
      wxPropertyGridConstIterator it( this, wxPG_ITERATE_DEFAULT, pwc->Item( 0 ) );
      it.SetBaseParent( pwc );
      for( ; !it.AtEnd(); it.Next() ) {
        const wxPGProperty* p = it.GetProperty();
        if( !p->GetChildCount() || p->HasFlag( wxPG_PROP_AGGREGATE ) ) {
          wxVariant variant = p->GetValue();
          variant.SetName( p->GetName() );
          v.Append( variant );
          if( ( flags & wxPG_INC_ATTRIBUTES ) && p->m_attributes.GetCount() ) {
            v.Append( p->GetAttributesAsList() );
          }
        }
      }
    }
  }
  return v;
}


void wxPropertyGridState::DoSetPropertyValues( const wxVariantList& list, wxPGProperty* defaultCategory ) {
  unsigned char origFrozen = 1;
  if( m_pPropGrid->GetState() == this ) {
    origFrozen = m_pPropGrid->m_frozen;
    if( !origFrozen ) {
      m_pPropGrid->Freeze();
    }
  }
  wxPropertyCategory* use_category = ( wxPropertyCategory* )defaultCategory;
  if( !use_category ) {
    use_category = ( wxPropertyCategory* )m_properties;
  }
  wxVariantList::const_iterator node;
  int numSpecialEntries = 0;
  for( node = list.begin(); node != list.end(); node++ ) {
    wxVariant *current = ( wxVariant* )*node;
    wxASSERT( current );
    wxASSERT( wxStrcmp( current->GetClassInfo()->GetClassName(), _T( "wxVariant" ) ) == 0 );
    const wxString& name = current->GetName();
    if( name.length() > 0 ) {
      if( name[0] == _T( '@' ) ) {
        numSpecialEntries++;
      } else {
        wxPGProperty* foundProp = BaseGetPropertyByName( name );
        if( foundProp ) {
          wxPGProperty* p = foundProp;
          if( wxStrcmp( current->GetType(), _T( "list" ) ) == 0 ) {
            DoSetPropertyValues( current->GetList(),
                                 p->IsCategory() ? p : ( ( wxPGProperty* )NULL )
                               );
          } else {
            #ifdef __WXDEBUG__
            if( wxStrcmp( current->GetType(), p->GetValue().GetType() ) != 0 ) {
              wxLogDebug( _T( "wxPropertyGridState::DoSetPropertyValues Warning: Setting value of property \"%s\" from variant" ),
                          p->GetName().c_str() );
            }
            #endif
            p->SetValue( *current );
          }
        } else {
          if( current->GetType() != _T( "list" ) ) {
          } else {
            wxPGProperty* newCat = DoInsert( use_category, -1, new wxPropertyCategory( current->GetName(), wxPG_LABEL ) );
            DoSetPropertyValues( current->GetList(), newCat );
          }
        }
      }
    }
  }
  if( numSpecialEntries ) {
    for( node = list.begin(); node != list.end(); node++ ) {
      wxVariant *current = ( wxVariant* )*node;
      const wxString& name = current->GetName();
      if( name.length() > 0 ) {
        if( name[0] == _T( '@' ) ) {
          numSpecialEntries--;
          size_t pos2 = name.rfind( _T( '@' ) );
          if( pos2 > 0 && pos2 < ( name.size() - 1 ) ) {
            wxString propName = name.substr( 1, pos2 - 1 );
            wxString entryType = name.substr( pos2 + 1, wxString::npos );
            if( entryType == _T( "attr" ) ) {
              wxPGProperty* foundProp = BaseGetPropertyByName( propName );
              if( foundProp ) {
                wxASSERT( wxPGIsVariantType( *current, list ) );
                wxVariantList& list2 = current->GetList();
                wxVariantList::const_iterator node2;
                for( node2 = list2.begin(); node2 != list2.end(); node2++ ) {
                  wxVariant *attr = ( wxVariant* )*node2;
                  foundProp->SetAttribute( attr->GetName(), *attr );
                }
              } else {
              }
            }
          } else {
          }
        }
      }
      if( !numSpecialEntries ) {
        break;
      }
    }
  }
  if( !origFrozen ) {
    m_pPropGrid->Thaw();
    if( this == m_pPropGrid->GetState() ) {
      m_pPropGrid->RefreshEditor();
    }
  }
}



void wxPGProperty::PrepareSubProperties() {
  wxPropertyGridState* state = GetParentState();
  wxASSERT( state );
  if( !GetCount() ) {
    return;
  }
  wxByte depth = m_depth + 1;
  wxByte depthBgCol = m_depthBgCol;
  FlagType inheritFlags = m_flags & wxPG_INHERITED_PROPFLAGS;
  wxByte bgColIndex = m_bgColIndex;
  wxByte fgColIndex = m_fgColIndex;
  size_t i = 0;
  wxPGProperty* nparent = this;
  while( i < nparent->GetCount() ) {
    wxPGProperty* np = nparent->Item( i );
    np->m_parentState = state;
    np->m_flags |= inheritFlags;
    np->m_depth = depth;
    np->m_depthBgCol = depthBgCol;
    np->m_bgColIndex = bgColIndex;
    np->m_fgColIndex = fgColIndex;
    if( np->GetCount() > 0 ) {
      nparent = np;
      i = 0;
      nparent->SetParentalType( wxPG_PROP_AGGREGATE );
      nparent->SetExpanded( false );
      depth++;
    } else
    { i++; }
    while( i >= nparent->GetCount() ) {
      if( nparent == this ) {
        break;
      }
      depth--;
      i = nparent->GetArrIndex() + 1;
      nparent = nparent->GetParent();
    }
  }
}



void wxPGProperty::SubPropsChanged( int oldSelInd ) {
  wxPropertyGridState* state = GetParentState();
  wxPropertyGrid* grid = state->GetGrid();
  PrepareSubProperties();
  wxPGProperty* sel = ( wxPGProperty* ) NULL;
  if( oldSelInd >= ( int )m_children.GetCount() ) {
    oldSelInd = ( int )m_children.GetCount() - 1;
  }
  if( oldSelInd >= 0 ) {
    sel = ( wxPGProperty* ) m_children[oldSelInd];
  } else if( oldSelInd == -2 ) {
    sel = this;
  }
  if( sel ) {
    state->DoSelectProperty( sel );
  }
  if( state == grid->GetState() ) {
    grid->GetPanel()->Refresh();
  }
}


int wxPropertyGridState::PrepareToAddItem( wxPGProperty* property,
    wxPGProperty* scheduledParent ) {
  wxPropertyGrid* propGrid = m_pPropGrid;
  if( scheduledParent == m_properties ) {
    scheduledParent = ( wxPGProperty* ) NULL;
  }
  if( scheduledParent && !scheduledParent->IsCategory() ) {
    wxASSERT_MSG( property->GetBaseName().length(),
                  _T( "Property's children must have unique, non-empty names within their scope" ) );
  }
  property->m_parentState = this;
  if( property->IsCategory() ) {
    wxASSERT_MSG( scheduledParent == NULL ||
                  scheduledParent == m_properties ||
                  scheduledParent->IsCategory(),
                  _T( "Parent of a category must be either root or another category." ) );
    wxPGProperty* found_id = BaseGetPropertyByName( property->GetBaseName() );
    if( found_id ) {
      wxPropertyCategory* pwc = ( wxPropertyCategory* ) found_id;
      if( pwc->IsCategory() ) {
        delete property;
        m_currentCategory = pwc;
        return 2;
      }
    }
  }
  #ifdef __WXDEBUG__
  if( BaseGetPropertyByName( property->GetName() ) &&
      ( !scheduledParent || scheduledParent->IsCategory() ) ) {
    wxLogError( _T( "wxPropertyGrid: Warning - item with name \"%s\" already exists." ),
                property->GetName().c_str() );
    wxPGGlobalVars->m_warnings++;
  }
  #endif
  if( scheduledParent ) {
    property->m_bgColIndex = scheduledParent->m_bgColIndex;
    property->m_fgColIndex = scheduledParent->m_fgColIndex;
    if( !scheduledParent->HasFlag( wxPG_PROP_PARENTAL_FLAGS ) ) {
      scheduledParent->SetParentalType( wxPG_PROP_MISC_PARENT );
    }
  }
  if(
    ( scheduledParent && ( scheduledParent->m_flags & wxPG_PROP_HIDDEN ) ) ||
    ( propGrid && ( propGrid->m_iFlags & wxPG_FL_ADDING_HIDEABLES ) )
  ) {
    property->SetFlag( wxPG_PROP_HIDDEN );
  }
  int custImgHeight = property->OnMeasureImage().y;
  if( custImgHeight < 0 ) {
    property->m_flags |= wxPG_PROP_CUSTOMIMAGE;
  }
  if( propGrid && ( propGrid->GetWindowStyleFlag() & wxPG_LIMITED_EDITING ) ) {
    property->m_flags |= wxPG_PROP_NOEDITOR;
  }
  if( !property->IsCategory() ) {
    unsigned char depth = 1;
    if( scheduledParent ) {
      depth = scheduledParent->m_depth;
      if( !scheduledParent->IsCategory() ) {
        depth++;
      }
    }
    property->m_depth = depth;
    unsigned char greyDepth = depth;
    if( scheduledParent ) {
      wxPropertyCategory* pc;
      if( scheduledParent->IsCategory() || scheduledParent->IsRoot() ) {
        pc = ( wxPropertyCategory* )scheduledParent;
      } else
      { pc = GetPropertyCategory( scheduledParent ); }
      if( pc ) {
        greyDepth = pc->GetDepth();
      } else
      { greyDepth = scheduledParent->m_depthBgCol; }
    }
    property->m_depthBgCol = greyDepth;
    if( property->GetCount() ) {
      property->SetParentalType( wxPG_PROP_AGGREGATE );
      property->SetExpanded( false );
      if( propGrid && propGrid->GetWindowStyleFlag() & wxPG_HIDE_MARGIN ) {
        property->SetExpanded( true );
      }
      property->PrepareSubProperties();
      return -1;
    }
    if( propGrid && ( propGrid->GetExtraStyle() & wxPG_EX_AUTO_UNSPECIFIED_VALUES ) ) {
      property->SetFlagRecursively( wxPG_PROP_AUTO_UNSPECIFIED, true );
    }
    return 0;
  } else {
    unsigned char depth = 1;
    if( scheduledParent ) {
      depth = scheduledParent->m_depth + 1;
    }
    property->m_depth = depth;
    property->m_depthBgCol = depth;
    m_currentCategory = ( wxPropertyCategory* )property;
    wxPropertyCategory* pc = ( wxPropertyCategory* )property;
    if( propGrid ) {
      pc->CalculateTextExtent( propGrid, propGrid->GetCaptionFont() );
    }
    return 1;
  }
}


void wxPropertyGridInterface::BeginAddChildren( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  wxCHECK_RET( p->HasFlag( wxPG_PROP_AGGREGATE ), _T( "only call on properties with fixed children" ) );
  p->ClearFlag( wxPG_PROP_AGGREGATE );
  p->SetFlag( wxPG_PROP_MISC_PARENT );
}


void wxPropertyGridInterface::EndAddChildren( wxPGPropArg id ) {
  wxPG_PROP_ARG_CALL_PROLOG()
  wxCHECK_RET( p->HasFlag( wxPG_PROP_MISC_PARENT ), _T( "only call on properties for which BeginAddChildren was called prior" ) );
  p->ClearFlag( wxPG_PROP_MISC_PARENT );
  p->SetFlag( wxPG_PROP_AGGREGATE );
}


wxPGProperty* wxPropertyGridState::DoAppend( wxPGProperty* property ) {
  wxPropertyCategory* cur_cat = m_currentCategory;
  if( property->IsCategory() ) {
    cur_cat = ( wxPropertyCategory* ) NULL;
  }
  return DoInsert( cur_cat, -1, property );
}


wxPGProperty* wxPropertyGridState::DoInsert( wxPGProperty* parent, int index, wxPGProperty* property ) {
  if( !parent ) {
    parent = m_properties;
  }
  wxCHECK_MSG( !parent->HasFlag( wxPG_PROP_AGGREGATE ),
               wxNullProperty,
               _T( "when adding properties to fixed parents, use BeginAddChildren and EndAddChildren." ) );
  int parenting = PrepareToAddItem( property, ( wxPropertyCategory* )parent );
  if( parenting > 1 ) {
    return m_currentCategory;
  }
  if( !parent->IsCategory() && !parent->IsRoot() ) {
    parent->AddChild2( property, index );
  } else {
    if( m_properties == &m_regularArray ) {
      if( m_abcArray && parenting <= 0 ) {
        m_abcArray->AddChild2( property, -1, false );
      }
      parent->AddChild2( property, index );
    } else {
      if( parent != m_properties ) {
        parent->AddChild2( property, index, false );
      } else
      { m_regularArray.AddChild2( property, -1, false ); }
      if( parenting <= 0 ) {
        m_abcArray->AddChild2( property, index );
      }
    }
  }
  if( property->IsCategory() ) {
    m_lastCaptionBottomnest = 0;
  }
  if( property->m_name.length() &&
      ( parent->IsCategory() || parent->IsRoot() ) ) {
    m_dictName[property->m_name] = ( void* ) property;
  }
  VirtualHeightChanged();
  property->UpdateParentValues();
  if( m_pPropGrid ) {
    m_pPropGrid->CorrectEditorWidgetPosY();
  }
  m_itemsAdded = 1;
  return property;
}


void wxPropertyGridState::DoDelete( wxPGProperty* item, bool doDelete ) {
  wxCHECK_RET( item->GetParent(),
               _T( "this property was already deleted" ) );
  wxCHECK_RET( item != &m_regularArray && item != m_abcArray,
               _T( "wxPropertyGrid: Do not attempt to remove the root item." ) );
  wxASSERT( item->GetParentState() == this );
  wxPropertyGrid* pg = GetGrid();
  if( pg && pg->m_processedEvent ) {
    if( doDelete ) {
      pg->m_deletedProperties.push_back( item );
    } else
    { pg->m_removedProperties.push_back( item ); }
    wxString newName = _T( "_&/_%$" ) + item->GetBaseName();
    pg->DoSetPropertyName( item, newName );
    return;
  }
  if( DoIsPropertySelected( item ) ) {
    if( pg && pg->GetState() == this ) {
      pg->DoRemoveFromSelection( item,
                                 wxPG_SEL_DELETING | wxPG_SEL_NOVALIDATE );
    } else
    { DoRemoveFromSelection( item ); }
  }
  item->SetFlag( wxPG_PROP_BEING_DELETED );
  unsigned int indinparent = item->GetIndexInParent();
  wxPGProperty* pwc = ( wxPGProperty* )item;
  wxPGProperty* parent = item->GetParent();
  wxCHECK_RET( !parent->HasFlag( wxPG_PROP_AGGREGATE ),
               _T( "wxPropertyGrid: Do not attempt to remove sub-properties." ) );
  if( item->GetChildCount() && !item->HasFlag( wxPG_PROP_AGGREGATE ) ) {
    if( item->IsCategory() ) {
      if( pwc == m_currentCategory ) {
        m_currentCategory = ( wxPropertyCategory* ) NULL;
      }
    }
    item->DeleteChildren();
  }
  if( !IsInNonCatMode() ) {
    if( !item->IsCategory() &&
        ( parent->IsCategory() || parent->IsRoot() ) ) {
      if( m_abcArray ) {
        m_abcArray->m_children.Remove( item );
      }
    }
    parent->m_children.RemoveAt( indinparent );
    parent->FixIndexesOfChildren();
  } else {
    wxPGProperty* cat_parent = &m_regularArray;
    int cat_index = m_regularArray.GetCount();
    size_t i;
    for( i = 0; i < m_regularArray.GetCount(); i++ ) {
      wxPGProperty* p = m_regularArray.Item( i );
      if( p == item ) {
        cat_index = i;
        break;
      }
      if( p->IsCategory() ) {
        int subind = ( ( wxPGProperty* )p )->Index( item );
        if( subind != wxNOT_FOUND ) {
          cat_parent = ( ( wxPGProperty* )p );
          cat_index = subind;
          break;
        }
      }
    }
    cat_parent->m_children.RemoveAt( cat_index );
    if( !item->IsCategory() ) {
      wxASSERT( item->m_parent == m_abcArray );
      item->m_parent->m_children.RemoveAt( indinparent );
      item->m_parent->FixIndexesOfChildren( indinparent );
    }
  }
  if( item->GetBaseName().length() &&
      ( parent->IsCategory() || parent->IsRoot() ) ) {
    m_dictName.erase( wxPGNameConv( item->GetBaseName() ) );
  }
  if( pg && pg->m_propHover == item ) {
    pg->m_propHover = NULL;
  }
  item->m_parentState = NULL;
  item->m_parent = NULL;
  if( doDelete ) {
    delete item;
  }
  m_itemsAdded = 1;
  VirtualHeightChanged();
}



void wxPropertyGridState::InitNonCatMode() {
  if( !m_abcArray ) {
    m_abcArray = new wxPGRootProperty();
    m_abcArray->SetParentState( this );
    m_abcArray->SetFlag( wxPG_PROP_CHILDREN_ARE_COPIES );
  }
  wxPGProperty* oldProperties = m_properties;
  m_properties = &m_regularArray;
  if( m_properties->GetChildCount() ) {
    wxPropertyGridIterator it( this, wxPG_ITERATE_PROPERTIES );
    for( ; !it.AtEnd(); it.Next() ) {
      wxPGProperty* p = it.GetProperty();
      wxPGProperty* parent = p->GetParent();
      if( parent->IsCategory() || parent->IsRoot() ) {
        m_abcArray->AddChild2( p );
        p->m_parent = &m_regularArray;
      }
    }
  }
  m_properties = oldProperties;
}


void wxPropertyGridState::DoClear() {
  if( m_pPropGrid && m_pPropGrid->GetState() == this ) {
    m_pPropGrid->DoSelectProperty( NULL,
                                   wxPG_SEL_DELETING | wxPG_SEL_NOVALIDATE );
  } else {
    m_selection.clear();
  }
  m_regularArray.DoEmpty();
  if( m_abcArray ) {
    m_abcArray->DoEmpty();
  }
  m_dictName.clear();
  m_currentCategory = ( wxPropertyCategory* ) NULL;
  m_lastCaptionBottomnest = 1;
  m_itemsAdded = 0;
  m_virtualHeight = 0;
  m_vhCalcPending = 0;
}


wxPropertyGridState::wxPropertyGridState() {
  m_pPropGrid = ( wxPropertyGrid* ) NULL;
  m_regularArray.SetParentState( this );
  m_properties = &m_regularArray;
  m_abcArray = ( wxPGRootProperty* ) NULL;
  m_currentCategory = ( wxPropertyCategory* ) NULL;
  m_width = 0;
  m_virtualHeight = 0;
  m_lastCaptionBottomnest = 1;
  m_itemsAdded = 0;
  m_anyModified = 0;
  m_vhCalcPending = 0;
  m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
  m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
  m_fSplitterX = wxPG_DEFAULT_SPLITTERX;
  m_columnProportions.push_back( 1 );
  m_columnProportions.push_back( 1 );
  m_editableColumns.push_back( 1 );
}


wxPropertyGridState::~wxPropertyGridState() {
  delete m_abcArray;
}


void wxPropertyGridState::CalculateFontAndBitmapStuff( int ( vspacing ) ) {
  wxPropertyGrid* propGrid = GetGrid();
  VirtualHeightChanged();
  unsigned int i;
  for( i = 0; i < m_regularArray.GetCount(); i++ ) {
    wxPGProperty* p = m_regularArray.Item( i );
    if( p->IsCategory() ) {
      ( ( wxPropertyCategory* )p )->CalculateTextExtent( propGrid, propGrid->GetCaptionFont() );
    }
  }
}



wxPropertyGridPopulator::wxPropertyGridPopulator() {
  m_state = NULL;
  m_pg = NULL;
  wxPGGlobalVars->m_offline++;
}


void wxPropertyGridPopulator::SetState( wxPropertyGridState* state ) {
  m_state = state;
  m_propHierarchy.clear();
}


void wxPropertyGridPopulator::SetGrid( wxPropertyGrid* pg ) {
  m_pg = pg;
  pg->Freeze();
}


wxPropertyGridPopulator::~wxPropertyGridPopulator() {
  wxPGHashMapS2P::iterator it;
  for( it = m_dictIdChoices.begin(); it != m_dictIdChoices.end(); ++it ) {
    wxPGChoicesData* data = ( wxPGChoicesData* ) it->second;
    data->DecRef();
  }
  if( m_pg ) {
    m_pg->Thaw();
    m_pg->GetPanel()->Refresh();
  }
  wxPGGlobalVars->m_offline--;
}


wxPGProperty* wxPropertyGridPopulator::Add( const wxString& propClass,
    const wxString& propLabel,
    const wxString& propName,
    const wxString* propValue,
    wxPGChoices* pChoices ) {
  wxClassInfo* classInfo = wxClassInfo::FindClass( propClass );
  wxPGProperty* parent = GetCurParent();
  if( parent->HasFlag( wxPG_PROP_AGGREGATE ) ) {
    ProcessError( wxString::Format( _T( "new children cannot be added to '%s'" ), parent->GetName().c_str() ) );
    return NULL;
  }
  if( !classInfo || !classInfo->IsKindOf( CLASSINFO( wxPGProperty ) ) ) {
    ProcessError( wxString::Format( _T( "'%s' is not valid property class" ), propClass.c_str() ) );
    return NULL;
  }
  wxPGProperty* property = ( wxPGProperty* ) classInfo->CreateObject();
  property->SetLabel( propLabel );
  property->DoSetName( propName );
  if( pChoices && pChoices->IsOk() ) {
    property->SetChoices( *pChoices );
  }
  m_state->DoInsert( parent, -1, property );
  if( propValue )
    property->SetValueFromString( *propValue, wxPG_FULL_VALUE |
                                  wxPG_PROGRAMMATIC_VALUE );
  return property;
}


void wxPropertyGridPopulator::AddChildren( wxPGProperty* property ) {
  m_propHierarchy.push_back( property );
  DoScanForChildren();
  m_propHierarchy.pop_back();
}


wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString,
    const wxString& idString ) {
  wxPGChoices choices;
  if( choicesString[0] == _T( '@' ) ) {
    wxString ids = choicesString.substr( 1 );
    wxPGHashMapS2P::iterator it = m_dictIdChoices.find( ids );
    if( it == m_dictIdChoices.end() ) {
      ProcessError( wxString::Format( _T( "No choices defined for id '%s'" ), ids.c_str() ) );
    } else
    { choices.AssignData( ( wxPGChoicesData* )it->second ); }
  } else {
    bool found = false;
    if( idString.length() ) {
      wxPGHashMapS2P::iterator it = m_dictIdChoices.find( idString );
      if( it != m_dictIdChoices.end() ) {
        choices.AssignData( ( wxPGChoicesData* )it->second );
        found = true;
      }
    }
    if( !found ) {
      wxString::const_iterator it = choicesString.begin();
      wxString label;
      wxString value;
      int state = 0;
      bool labelValid = false;
      for( ; it != choicesString.end(); it++ ) {
        wxChar c = *it;
        if( state != 1 ) {
          if( c == _T( '"' ) ) {
            if( labelValid ) {
              long l;
              if( !value.ToLong( &l, 0 ) )
              { l = wxPG_INVALID_VALUE; }
              choices.Add( label, l );
            }
            labelValid = false;
            value.clear();
            label.clear();
            state = 1;
          } else if( c == _T( '=' ) ) {
            if( labelValid )
            { state = 2; }
          } else if( state == 2 && ( wxIsalnum( c ) || c == _T( 'x' ) ) ) {
            value << c;
          }
        } else {
          if( c == _T( '"' ) ) {
            state = 0;
            labelValid = true;
          } else
          { label << c; }
        }
      }
      if( labelValid ) {
        long l;
        if( !value.ToLong( &l, 0 ) ) {
          l = wxPG_INVALID_VALUE;
        }
        choices.Add( label, l );
      }
      if( !choices.IsOk() ) {
        choices.EnsureData();
      }
      if( idString.length() ) {
        m_dictIdChoices[idString] = choices.GetData();
      }
    }
  }
  return choices;
}


bool wxPropertyGridPopulator::ToLongPCT( const wxString& s, long* pval, long max ) {
  if( s.Last() == _T( '%' ) ) {
    wxString s2 = s.substr( 0, s.length() - 1 );
    long val;
    if( s2.ToLong( &val, 10 ) ) {
      *pval = ( val * max ) / 100;
      return true;
    }
    return false;
  }
  return s.ToLong( pval, 10 );
}


bool wxPropertyGridPopulator::AddAttribute( const wxString& name,
    const wxString& type,
    const wxString& value ) {
  int l = m_propHierarchy.size();
  if( !l ) {
    return false;
  }
  wxPGProperty* p = m_propHierarchy[l - 1];
  wxString valuel = value.Lower();
  wxVariant variant;
  if( type.length() == 0 ) {
    long v;
    if( valuel == _T( "true" ) || valuel == _T( "yes" ) || valuel == _T( "1" ) ) {
      variant = true;
    } else if( valuel == _T( "false" ) || valuel == _T( "no" ) || valuel == _T( "0" ) ) {
      variant = false;
    } else if( value.ToLong( &v, 0 ) ) {
      variant = v;
    } else
    { variant = value; }
  } else {
    if( type == _T( "string" ) ) {
      variant = value;
    } else if( type == _T( "int" ) ) {
      long v = 0;
      value.ToLong( &v, 0 );
      variant = v;
    } else if( type == _T( "bool" ) ) {
      if( valuel == _T( "true" ) || valuel == _T( "yes" ) || valuel == _T( "1" ) ) {
        variant = true;
      } else
      { variant = false; }
    } else {
      ProcessError( wxString::Format( _T( "Invalid attribute type '%s'" ), type.c_str() ) );
      return false;
    }
  }
  p->SetAttribute( name, variant );
  return true;
}


void wxPropertyGridPopulator::ProcessError( const wxString& msg ) {
  wxLogError( _T( "Error in resource: %s" ), msg.c_str() );
}
